操作系统实验五
1概要设计
1.1 I/O系统
实际物理磁盘的结构是多维的:有柱面、磁头、扇区等概念。I/O系统的任务是隐藏磁盘的结构细节,把磁盘以逻辑块的面目呈现给文件系统。逻辑块顺序编号,编号取值范围为0至L−1,其中L表示磁盘的存储块总数。实验中,我们可以利用数组ldisk[C][H][B]构建磁盘模型,其中CHB 分别表示柱面号,磁头号和扇区号。每个扇区大小为512字节。I/O系统从文件系统接收命令,根据命令指定的逻辑块号把磁盘块的内容读入命令指定的内存区域,或者把命令指定的内存区域内容写入磁盘块。
I/O系统需要提供以下函数:read_block,write_block。
•read_block(int i,char *p):把逻辑块i的内容读入到指针p指向的内存位置,拷贝的字符个数为存储块的长度B。
•write_block(int i,char *p):该函数把指针p指向的内容写入逻辑块i,拷贝的字符个数为存储块的长度B。
1.2 文件系统
文件系统位于I/O系统之上。
磁盘的前k个块是保留区,其中包含如下信息:位图和文件描述符。位图用来描述磁盘块的分配情况。位图中的每一位对应一个逻辑块。创建或者删除文件,以及文件的长度发生变化时,文件系统都需要进行位图操作。前k个块的剩余部分包含一组文件描述符。每个文件描述符包含如下信息:
• 文件长度,单位字节
• 文件分配到的磁盘块号数组。该数组的长度是一个系统参数。在实验中我们可以把它设置为一个比较小的数,例如3。
文件系统需提供如下函数:create, destroy, open, close,read, write。
• create(filename): 根据指定的文件名创建新文件。
• destroy(filename): 删除指定文件。
• open(filename): 打开文件。该函数返回的索引号可用于后续的read, write, lseek,或close操作。
• close(index): 关闭制定文件。
• read(index, mem_area, count): 从指定文件顺序读入count个字节memarea指定的内存位置。读操作从文件的读写指针指示的位置开始。
• write(index, mem_area, count): 把memarea指定的内存位置开始的count个字节顺序写入指定文件。写操作从文件的读写指针指示的位置开始。
• lseek(index, pos): 把文件的读写指针移动到pos指定的位置。pos是一个整数,表示从文件开始位置的偏移量。文件打开时,读写指针自动设置为0。每次读写操作之后,它指向最后被访问的字节的下一个位置。lseek能够在不进行读写操作的情况下改变读写指针能位置。
• directory: 列表显示所有文件及其长度。
2详细设计
2.1 I/O系统
把逻辑块i的内容读入到指针p指向的内存位置,或把指针p指向的内容写入到逻辑块i,每次拷贝的字符个数为存储块的长度B。
2.2文件系统
2.2.1文件的创建与删除
创建文件时需要进行如下操作;
• 找一个空闲文件描述符(扫描ldisk [0]~ldisk [k - 1])
• 在文件目录里为新创建的文件分配一个目录项(可能需要为目录文件分配新的磁盘块)
• 在分配到的目录项里记录文件名及描述符编号.
• 返回状态信息(如有无错误发生等)
删除文件时需要进行如下操作(假设文件没有被打开):
• 在目录里搜索该文件的描述符编号
• 删除该文件对应的目录项并更新位图
• 释放文件描述符
• 返回状态信息
2.2.2文件的打开与关闭
文件系统维护一张打开文件表.打开文件表的长度固定,其表目包含如下信息:
• 读写缓冲区
• 读写指针
• 文件描述符号
文件被打开时,便在打开文件表中为其分配一个表目;
文件被关闭时,其对应的表目被释放。读写缓冲区的大小等于一个磁盘存储块。
打开文件时需要进行的操作如下:
• 搜索目录找到文件对应的描述符编号
• 在打开文件表中分配一个表目
• 在分配到的表目中把读写指针置为0,并记录描述符编号
• 读入文件的第一块到读写缓冲区中
• 返回分配到的表目在打开文件表中的索引号
关闭文件时需要进行的操作如下:
• 把缓冲区的内容写入磁盘
• 释放该文件在打开文件表中对应的表目
• 返回状态信息
2.2.3文件读写
文件打开之后才能进行读写操作.读操作需要完成的任务如下:
- 计算读写指针对应的位置在读写缓冲区中的偏移
- 把缓冲区中的内容拷贝到指定的内存位置,直到发生下列事件之一:
• 到达文件尾或者已经拷贝了指定的字节数。这时,更新读写指针并返回相应信息
• 到达缓冲区末尾。这时,把缓冲区内容写入磁盘,然后把文件下一块的内容读入磁盘。最后返回第2步。
2.2.4文件目录
我们的文件系统中仅设置一个目录,该目录包含文件系统中的所有文件。除了不需要显示地创建和删除之外,目录在很多方面和普通文件相像。目录对应0号文件描述符。初始状态下,目录中没有文件,所有,目录对应的描述符中记录的长度应为0,而且也没有分配磁盘块。每创建一个文件,目录文件的长度便增加一分。目录文件的内容由一系列的目录项组成,其中每个目录项由如下内容组成:
• 文件名
• 文件描述符序号
3实验实现
3.1 I/O系统
3.1.1 read_block
void read_block(int i, char *p)//把逻辑块i的内容读入到指针P指向的内存位置,每次读入B块
{
char *temp = (char *)malloc(sizeof(char));
temp = p;
for (int j = 0; j < B;j++)
{
*temp = ldisk[i][j];
temp++;
}
}
3.1.2 write_block
void write_block(int i, char *p)//把指针P的内容写入逻辑块i,每次写入B块
{
char *temp = (char*)malloc(sizeof(char));
temp = p;
for (int j = 0; j < B; j++)
{
ldisk[i][j] =*temp;
temp++;
}
}
3.2 文件系统
3.2.1 文件的创建与删除
文件创建代码:
int create(char filename[])//创建文件
{
int i, frees, freed, freed2;
char temps[B], tempc[B], temp[B];
for (i = K; i < K + FILE_NUM; i++)
{
read_block((i - K) / B, temp);
if (temp[(i - K) % B] == BUSY)
{
read_block(i, temp);
if (strncmp(temp + 1, filename, FILE_NAME_LENGTH) == 0)
{
printf("该目录已存在文件名为%s的文件\n", filename);
return ERROR;
}
}
}
for (i = FILE_SIGN_AREA; i < K; i++)
{
read_block(i, temp);
if (temp[0] == FREE)
{
frees = i;
break;
}
}
if (i == K)
{
printf("没有空闲的文件描述符\n");
return ERROR;
}
for (i = K; i < K + FILE_NUM; i++)
{
read_block((i - K) / B, temp);
if (temp[(i - K) % B] == FREE)
{
freed = i;
break;
}
}
if (i == K + FILE_NUM)
{
printf("文件数已达上限\n");
return ERROR;
}
for (i = K + FILE_NUM; i < L; i++)
{
read_block((i - K) / B, temp);
if (temp[(i - K) % B] == FREE)
{
freed2 = i;
break;
}
}
if (i == L)
{
printf("磁盘已满,分配失败\n");
return ERROR;
}
filesign temp_filesign;
contents temp_contents;
temp_filesign.filesign_flags = 1;
temp_filesign.file_length = 0;
temp_filesign.file_block = 1;
init_block(temps, B);
temps[0] = temp_filesign.filesign_flags;
temps[1] = temp_filesign.file_length;
temps[2] = temp_filesign.file_block;
temps[3] = freed2;
for (i = 4; i < FILE_BLOCK_LENGTH; i++)
{
temps[i] = '\0';
}
write_block(frees, temps);
temp_contents.filesignum = frees - FILE_SIGN_AREA;
strncpy(temp_contents.filename, filename, FILE_NAME_LENGTH);
init_block(tempc, B);
tempc[0] = temp_contents.filesignum;
tempc[1] = '\0';
strcat(tempc, temp_contents.filename);
write_block(freed, tempc);
read_block((freed - K) / B, temp);
temp[(freed - K) % B] = BUSY;
write_block((freed - K) / B, temp);
read_block((freed2 - K) / B, temp);
temp[(freed2 - K) % B] = BUSY;
write_block((freed2 - K) / B, temp);
read_block(FILE_SIGN_AREA, temp);
temp[1]++;
write_block(FILE_SIGN_AREA, temp);
return OK;
}
文件删除代码:
int destroy(char *filename)
{
int i, dtys, dtyd, use_block, index;
char temp[B];
char tempd[B];
for (i = K; i < K + FILE_NUM; i++)
{
read_block((i - K) / B, temp);
if (temp[(i - K) % B] == BUSY)
{
read_block(i, temp);
if (strncmp(temp + 1, filename, FILE_NAME_LENGTH) == 0)
{
dtyd = i;
dtys = temp[0] + FILE_SIGN_AREA;
index = temp[0];
break;
}
}
}
if (i == K + FILE_NUM)
{
printf("没有找到该文件\n");
return ERROR;
}
int list = -1;
for (i = 0; i < FILE_NUM; i++)
{
if (open_list[i].filesigunm == index)
{
list = i;
break;
}
}
if (open_list[list].flag == BUSY && list != -1)
{
printf("该文件已经被打开,需要关闭才能删除\n");
return ERROR;
}
read_block(dtys, temp);
use_block = temp[2];
for (i = 0; i < use_block; i++)
{
read_block((temp[i + 3] - K) / B, tempd);
tempd[(temp[i + 3] - K) % B] = FREE;
write_block((temp[i + 3] - K) / B, tempd);
}
init_block(temp, B);
write_block(dtys, temp);
init_block(temp, B);
write_block(dtyd, temp);
read_block((dtyd - K) / B, temp);
temp[(dtyd - K) % B] = FREE;
write_block((dtyd - K) / B, temp);
read_block(FILE_SIGN_AREA, temp);
temp[1]--;
write_block(FILE_SIGN_AREA,temp);
return OK;
}
3.2.2 文件的打开与关闭
打开文件的代码:
int open(char *filename)
{
int i, opd, ops, list, index;
char temp[B];
for (i = K; i < K + FILE_NUM; i++)
{
read_block((i - K) / B, temp);
if (temp[(i - K) % B] == BUSY)
{
read_block(i, temp);
if (strncmp(temp + 1, filename, FILE_NAME_LENGTH) == 0)
{
opd = i;
ops = temp[0];
break;
}
}
}
if (i == K + FILE_NUM)
{
printf("没有找到该文件\n");
return ERROR;
}
for (i = 0; i < FILE_NUM; i++)
{
if (open_list[i].filesigunm == ops && open_list[i].flag == BUSY)
{
printf("该文件已被打开\n");
return ERROR;
}
}
for (i = 0; i < FILE_NUM; i++)
{
if (open_list[i].flag != BUSY)
{
list = i;
break;
}
}
open_list[list].filesigunm = ops;
open_list[list].flag = BUSY;
index = open_list[list].filesigunm;
lseek(index, 0);
init_block(open_list[list].buffer, BUFFER_LENGTH);
read_block(open_list[list].pointer[0], temp);
strncpy(open_list[list].buffer, temp, BUFFER_LENGTH);
return OK;
}
关闭文件的代码
int close(int index)
{
int i;
int list = -1;
char temp[B];
for (i = 0; i < FILE_NUM; i++)
{
if (open_list[i].filesigunm == index)
{
list = i;
break;
}
}
if (list == -1)
{
printf("没找到当前做引号文件,操作失败\n");
return ERROR;
}
if (open_list[list].flag != BUSY)
{
printf("输入索引号有误,操作失败\n");
return ERROR;
}
write_buffer(index, list);
init_block(open_list[list].buffer, BUFFER_LENGTH);
open_list[list].filesigunm = FREE;
open_list[list].flag = FREE;
open_list[list].pointer[0] = NULL;
open_list[list].pointer[1] = NULL;
return OK;
}
3.2.3 文件读写
读文件的代码:
int read(int index, int mem_area, int count)
{
int i;
int list = -1;
char temp[B];
for (i = 0; i < FILE_NUM; i++)
{
if (open_list[i].filesigunm == index)
{
list = i;
break;
}
}
if (list == -1)
{
printf("没找到当前索引号文件,操作失败\n");
return ERROR;
}
if (open_list[list].flag != BUSY)
{
printf("输入的索引号有误,操作失败\n");
return ERROR;
}
char temp_output[OUTPUY_LENGTH];
init_block(temp_output, OUTPUY_LENGTH);
char output[OUTPUY_LENGTH];
init_block(output, OUTPUY_LENGTH);
read_block(FILE_SIGN_AREA + index, temp);
int file_length = temp[1];
int file_block = temp[2];
int file_area;
for (i = 0; i < file_block - 1; i++)
{
read_block(FILE_SIGN_AREA + index, temp);
read_block(temp[3 + i], temp);
strncpy(temp_output + i * B, temp, B);
}
read_block(FILE_SIGN_AREA+index, temp);
read_block(temp[3 + i], temp);
strncpy(temp_output + i * B, temp, B);
int x = open_list[list].pointer[0];
int y = open_list[list].pointer[1];
for (i = 0; i < file_block; i++)
{
read_block(FILE_SIGN_AREA + index, temp);
if (temp[3 + i] == x)
{
break;
}
}
file_area = i * B + y;
for (i = 0; i < count; i++)
{
output[i + mem_area] = temp_output[i + file_area];
}
printf("%s\n", output + mem_area);
return OK;
}
写文件的代码:
int write(int index, int mem_area, int count)
{
int i;
int list = -1;
int input_length;
char temp[B];
for (i = 0; i < FILE_NUM; i++)
{
if (open_list[i].filesigunm == index)
{
list = i;
break;
}
}
if (list == -1)
{
printf("没找到当前索引号文件,操作失败\n");
return ERROR;
}
if (open_list[list].flag != BUSY)
{
printf("输入索引号错误,操作失败\n");
return ERROR;
}
char input[INPUT_LENGTH];
init_block(input, INPUT_LENGTH);
fflush(stdin);
for(i=0;;i++)
{
scanf("%c", &input[i]);
if (input[i] == '\n')
{
input[i] = '\0';
break;
}
}
while (scanf("%c", &input[i]))
{
if (input[i] == '\n')
{
input[i] = '\0';
break;
}
i++;
}
input_length = i;
if (count <= BUFFER_LENGTH)
{
strncat(open_list[list].buffer, input + mem_area, count);
}
else
{
int rest;
for (i = 0; i < BUFFER_LENGTH; i++)
{
if (open_list[list].buffer[i] == FREE)
{
rest = BUFFER_LENGTH - i;
break;
}
}
strncat(open_list[list].buffer + BUFFER_LENGTH - rest, input + mem_area, rest);
write_buffer(index, list);
init_block(open_list[list].buffer, BUFFER_LENGTH);
for (i = 0; i < (count / BUFFER_LENGTH) - 1; i++)
{
strncpy(open_list[list].buffer, (input + mem_area) + rest + i * BUFFER_LENGTH, BUFFER_LENGTH);
write_buffer(index, list);
init_block(open_list[list].buffer, BUFFER_LENGTH);
}
init_block(open_list[list].buffer, BUFFER_LENGTH);
strncpy(open_list[list].buffer, (input + mem_area) + rest + i*BUFFER_LENGTH, count%BUFFER_LENGTH);
int buffer_start;
}
return OK;
}
3.2.4 文件目录
void directory()
{
int i, filenum, filelength;
char filename[FILE_NAME_LENGTH], temp[B], tempd[B], temps[B];
read_block(FILE_SIGN_AREA, temp);
filenum = temp[1];
printf("\n");
if (filenum == 0)
{
printf("该目录下没有文件\n");
}
for (i = 0; i < FILE_NUM; i++)
{
read_block(temp[3 + i], tempd);
if (tempd[0] != 0)
{
read_block(tempd[0] + FILE_SIGN_AREA, temps);
if (temps[0] == BUSY && tempd[0] != 0)
{
filelength = temps[1];
strcpy(filename, tempd + 1);
printf("%s\t\t%d字节\n", filename, filelength);
}
}
}
if (filenum != 0)
{
printf("\t\t\t\t共%d个文件\n", filenum);
}
}
3.3实验结果
菜单目录
创建文件
查看文件目录
打开文件
查看已打开文件
关闭文件
首先我们输入关闭文件命令,将我们刚刚打开的文件“yuan”关闭,在输入命令查看一下已打开文件列表,可以看到,无文件打开,操作成功。
删除文件
首先我们输入删除文件命令,将我们刚刚创建的文件“yuan”关闭,在输入命令查看一下文件列表,可以看到,仅剩一个文件,我们刚刚创建的文件“yuan”已被删除。
读取文件内容
读取文件内容时,我们通过文件的索引号来确定文件,同时也需要读取文件内容的长度。
ps:由于这里之前未写入文件,所以读取内容为空。
修改文件内容
修改文件内容前,我们首先需要打开一个文件,可以看到,我们第一次操作时,由于未打开文件,所以操作失败。修改文件时,我们需要通过文件的索引号定位文件。
查看磁盘信息
本实验完整源码已完全上传至github
如若上述超链接无法打开,可直接复制如下地址访问: https://github.com/16281307/OS/tree/master/lab5