实验介绍
本实验要求在假设的I/O 系统之上开发一个简单的文件系统,这样做既能让实验者对文件系统有整体了解,又避免了涉及过多细节。用户通过create, open, read 等命令与文件系统交互。文件系统把磁盘视为顺序编号的逻辑块序列,逻辑块的编号为0 至L-1。I/O 系统利用内存中的数组模拟磁盘。
实际物理磁盘的结构是多维的:有柱面、磁道、扇区等概念。I/O 系统的任务是隐藏磁盘的结构细节,把磁盘以逻辑块的面目呈现给文件系统。逻辑块顺序编号,编号取值范围为0 至L -1,其中L 表示磁盘的存储块总数。实验中,我们可以利用字符数组ldisk[C][H][B] 构建磁盘模型,其中B 表示每个存储块的长度。I/O 系统从文件系统接收命令,根据命令指定的逻辑块号把磁盘块的内容读入命令指定的内存区域,或者把命令指定的内存区域内容写入磁盘块。内存区域内容写入磁盘块。
整体组织
注:我定义的文件系统中,磁盘分为两大部分:数据区和保留区。其中保留区中又包含位图区和文件描述符区,数据区的首部是文件的目录项,也就是说,文件的目录项在文件创建时会创建相应的目录项在数据区的文件首部;而位图区用于表征数据的占用情况,例如数据区的第N块被分配了,那么位图区中也要做相应的改变。
文件描述符
注:文件描述符位于保留区后半部,用于分为两类型,即表示目录的0号描述符和1号以后的描述符,用于表示文件的长度和分配块情况,具体看上图。
目录项
注:目录项位于数据区的首部,目录项的0位用于保存文件描述符的序号,1号以后用于存放文件名
总结:
一个文件的所有存放于由位视图,文件描述符,目录项和数据区表征。
①对磁盘的操作:
字符数组(L*B)模拟磁盘,考虑到文件系统和I/O系统(磁盘)不是同一层,故将所有对磁盘的操作只能规约为这二者之间的接口来操作:
void read_block(int,char *); //文件系统与IO设备的接口函数,读取块
void write_block(int,char *); //文件系统与IO设备的接口函数,写入块
(代码详见源文件)
也就是说,之后的文件的创建,删除,打开,关闭,指针定位等操作,里面牵扯到磁盘内容读写的步骤,全部需要用这两个接口完成,然后这两个接口对磁盘的操作是以块为单位的,所以例如某些操作,只要更改磁盘中的某一块中的某一位,则需要先读取某一块存于零时数组中,然后修改这个数组的某一位,然后再将这个数组写回磁盘。
②对文件的操作:
文件的创建int create(char *)
.找一空闲文件描述符
.在文件目录里为新创建的文件分配一个目录项,(可能需要为目录文件分配新的磁盘块)
.在分配到的目录项里记录文件名以及描述符编号
.返回状态信息
文件的删除int destroy(char *)
.在目录里搜索该文件的描述符编号
.删除该文件对应的目录项,并更新位图
.释放文件描述符
.返回状态信息
文件的打开int open(char *)
.搜索目录找到文件对应的描述符序号
.在打开文件表中分配一个表目
.在分配到的表目中把读写指针置为0,并记录描述符编号
.读入文件的第一块到读写缓冲区中
.返回分配到的表目在打开文件表中的索引号
文件的关闭int close(int)
.把缓冲区的内容写入磁盘
.释放该文件再打开文件表中对应的表目
.返回状态信息
文件的写int write(int,int,int)
.写的内容若小于缓冲区内容,则直接写入缓冲区,完成写操作(关闭的时候会将缓冲区写入文件)
.入写入的内容,缓冲区无法一次装完,则需要填满缓冲区,然后将缓冲区写入文件,腾出缓冲区,然后再写入缓冲区,碰到缓冲区满的情况便写入文件,腾出缓冲区,以 便文件的写入,完成写操作。
.返回状态信息
文件的读int read(int,int,int)
.将文件整个内容整个取出在一个临时字符数组中
.根据要求的读取参数定位读写位置,输出读取内容,完成读操作
.返回状态信息
文件指针定位int lseek(int,int)
.把文件的读写指针移动到pos 指定的位置。pos
.是一个整数,表示从文件开始位置的偏移量。文件打开时,读写指针
.自动设置为0。每次读写操作之后,它指向最后被访问的字节的下一
.个位置。lseek 能够在不进行读写操作的情况下改变读写指针能位置。
实验过程
该文件系统根据输入的指令来对文件系统的操作
此时该文件系统会初始化
新建文件操作:(create)
显示目录文件:(dir)
文件的删除:delete
打开文件:(open)
打开文件表:(oplist)
文件的写操作:(write)
关闭文件:close
打开和读取lmjorz:
后台查看磁盘数组的内容,可以查看实时情况命令(ldisk)
实验源代码:
operation.h
//#pragma warning( disable : 4996)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int show_openlist();
void directory();
void show_help();
void show_ldisk();
//核心函数
void read_block(int, char *);
void write_block(int, char *);
void Init();
int create(char *);
int destroy(char *);
int open(char *);
int close(int);
int read(int, int, int);
int write(int, int, int);
int write_buffer(int, int);
int lseek(int, int);
void Init_block(char, int);
/*****************************************************/
/*****************************************************/
void read_block(int i, char *p)
/**************************读磁盘块
该函数把逻辑块的内容读入到指针指向的内存位置
拷贝的字符个数为存储块的长度。
***************************/
{
char * temp = (char *)malloc(sizeof(char));
temp = p;
for (int a = 0; a < B;)
{
*temp = ldisk[i][a];
a++;
temp++;
}
}
void write_block(int i, char *p)
/**************************写磁盘块
该函数把指针p指向的内容写入逻辑块i
拷贝的字符个数为a存储块的长度。
***************************/
{
char * temp = (char *)malloc(sizeof(char));
temp = p;
for (int a = 0; a < B;)
{
ldisk[i][a] = *temp;
a++;
temp++;
}
}
void Init_block(char *temp, int length)
/**************************初始化一个字符数组块
处理的字符数组块长度为a
内容为'\0'
***************************/
{
int i;
for (i = 0; i < length; i++)
{
temp[i] = '\0';
}
}
int write_buffer(int index, int list)
{
int i;
int j;
int freed;
char temp[B];
int buffer_length = BUFFER_LENGTH;
for (i = 0; i < BUFFER_LENGTH; i++)
{
if (open_list[list].buffer[i] == '\0')
{
buffer_length = i;
break;
}
}
int x = open_list[list].pointer[0];
int y = open_list[list].pointer[1];
int z = B - y;
if (buffer_length < z) {
read_block(x, temp);
strncat_s(temp + y,sizeof(temp + y+ buffer_length), open_list[list].buffer, buffer_length);
write_block(x, temp);
read_block(index + FILE_SIGN_AREA, temp);
temp[1] += buffer_length;
write_block(index + FILE_SIGN_AREA, temp);
open_list[list].pointer[0] = x;
open_list[list].pointer[1] = y + buffer_length;
}
else
{
read_block(index + FILE_SIGN_AREA, temp);
if (temp[2] + (buffer_length - z) / B + 1 > FILE_BLOCK_LENGTH)
{
printf("文件分配数组不够分配\n");
return ERROR;
}
read_block(x, temp);
strncat_s(temp + y,sizeof(temp + y + z), open_list[list].buffer, z); write_block(x, temp);
//******************寻找文件区(目录项之后)的空闲块,分配新块
for (i = 0; i < (buffer_length - z) / B; i++)
{
for (j = K + FILE_NUM; j < L; j++)
{
read_block((j - K) / B, temp);
if (temp[(j - K) % B] == FREE)
{
freed = j;
break;
}
}
if (j == L)
{
printf("磁盘已满,分配失败\n");
return ERROR;
}
Init_block(temp, B);
strncpy_s(temp, (open_list[list].buffer + z + (i*B)), B);
write_block(freed, temp);
read_block((freed - K) / B, temp);
temp[(freed - K) % B] = BUSY;
write_block((freed - K) / B, temp);
read_block(index + FILE_SIGN_AREA, temp);
temp[2] ++;
temp[2 + temp[2]] = freed;
write_block(index + FILE_SIGN_AREA, temp);
}
for (j = K + FILE_NUM; j < L; j++)
{
read_block((j - K) / B, temp);
if (temp[(j - K) % B] == FREE)
{
freed = j;
break;
}
}
if (j == L)
{
printf("磁盘已满,分配失败\n");
return ERROR;
}
Init_block(temp, B);
strncpy_s(temp, (open_list[list].buffer + z + (i*B)), (buffer_length - z) % B);
write_block(freed, temp);
read_block((freed - K) / B, temp);
temp[(freed - K) % B] = BUSY;
write_block((freed - K) / B, temp);
read_block(index + FILE_SIGN_AREA, temp);
temp[2] ++;
temp[2 + temp[2]] = freed;
write_block(index + FILE_SIGN_AREA, temp);
read_block(index + FILE_SIGN_AREA, temp);
temp[1] += buffer_length;
write_block(index + FILE_SIGN_AREA, temp);
open_list[list].pointer[0] = freed;
open_list[list].pointer[1] = (buffer_length - z) %