V 模拟文件系统
1 项目概述
2 输入/输出系统
3 文件系统
4 演示shell程序
5 具体任务总结
6 附加任务的建议
1 项目概述
在本项目中,使用一个模拟的输入/输出系统来开发一个简单的文件系统。下图给出了基本结构:
用户使用命令同文件系统交互,如create、open或者read文件。文件系统将磁盘看作从0~L-1编号的逻辑块的线性序。输入/输出系统使用一个内存数组来模拟磁盘,并将逻辑块的抽象作为其接口提供给文件系统。
2 输入/输出系统
物理磁盘是一个由柱面、柱面里的磁道、磁道里的扇区和扇区里的字节的组成的三维结构。输入/输出系统的任务是,通过将磁盘
示为一个标号从0~L-1的逻辑块线性系列从而隐藏其三维组织方式,其中L是物理磁盘上磁盘块的总数目。
我们将用一个字符数组ldisk[L][B]来模拟磁盘,其中L是逻辑块数,B是块长度,即每个块的字节数。输入/输出系统的任务就是从文件系统接收逻辑块号,并将相应的块读入或者写入由命令指定的内存区域中。
使用如下两个函数来定义文件系统和输入/输出系统之间的接口,只要系统读写磁盘块,它就要调用这两个函数:
· read_block(int i, char *p);
复制逻辑块ldisk[i]到以指针p指定的位置开始的内存中。复制的字符数与块长度B相。i
· write_block(int i, char *p)
从指针p指定的位置开始的内存中,复制与块长度B相等的字符数到逻辑块ldisk[i]中。
此外,我们要实现其它两个函数:一个是将数组保存到一个文件中,另一个是将该文件中的内容读到该数组。这样便使得我们在没有登录时就可以保存磁盘的内容。
3 文件系统
文件系统是建立在上述模拟的输入/输出系统的基础上的。
3.1 用户和文件系统之间的接口
文件系统必须支持以下函数:create、destory、open、read、write、lseek和directory。
· create(symbolic_file_name):使用指定的名称创建一个新文件。
· destory(symbolic_file_name):删除指定名称的文件。
· open(symbolic_file_name):为了读和写而打开指定名称的文件;返回一个索引值,供随后的read、write、lseek或者close操作使用。
· close(index):关闭指定文件。
· read(index,mem_area,count):从指定的文件中顺序读取一定数目的字节到内存中。所读的字节数在count中指定,开始的内存地址在mem_area中。读操作从文件的当前位置开始。
· write(index,mem_area,count):把从mem_area开始的内存中的一定数目的字节写入到指定的文件中。如同使用读操作一样,字节数在count中指定。写操作从文件的当前位置开始。
· lseek(index,pos):将文件的当前位置移动到pos,其中pos是从文件开始的指定的字节数的一个整数。当文件最初被打开时,当前位置自动设为0。每一次读或写操作,它马上指向跟在最后被访问字节的后一字节。Lseek允许在不必读写数据时,明确地改变位置。查找到位置0便实现了复位命令,从而可以从开始处重新读或者重新写整个文件。
· directory:列出所有文件的名称及其长度。
3.2 文件系统的组织结构
磁盘的前k块磁盘块是保留的,它们包括以下描述的信息:
ldisk[0]..[k-1] 文件描述符 ldisk[k]
目录描述符
位图说明了哪些磁盘块是空闲的以及哪些已被现有文件占用了。位图中的每一位对应于一个逻辑磁盘块。只要写操作使得文件增加或者当删除文件时,文件系统都要查询位图。(注意,文件从来不会缩小。减少其长度的唯一
是将相关部分 复制到一个新文件中,并删除原来的文件。)
前k个磁盘块中剩余的部分包括了一个文件描述数组。描述符的最大数由块的长度和k的数目来决定。每一个描述符都包括以下信息:
· 以字节表示的文件长度;
· 含有文件内容的磁盘块的块号的一个数组。数组的长度是一个系统参数,将该值设为一个很小的数,如3。
3.3 目录
只有一个目录文件来记录所有的文件。除了目录文件从来不能被明确地创建或删除以外,它就象一个普通文件一样。它对应于磁盘上的第一个文件描述符(如图所示)。初始没有文件时,它的长度为0 ,没有分配磁盘块。当文件被创建时,它就会被扩充。
目录文件以一个记录项的数组的方式组织。每个记录项都包括如下信息:
· 符号文件名;
· 文件描述符索引。
3.4 创建和删除文件
create例程执行的主要任务有:
· 找到空闲的文件描述符(读入并从ldisk[0]扫描到ldisk[k-1])
· 在目录中找到空闲的记录项(可以通过回绕并读取目录直到找到一个空闲的位置来完成;前面曾提到,对待目录同其它的文件一样)。同时,验证该文件是否已经存在。如果存在,返回错误状态。
· 将符号文件名和描述符索引写入到目录中。
· 返回状态信息。
为了删除文件,要做以下工作(假设调用删除例程时文件不会被打开):
· 通过查询目录找到文件描述符;
· 移除目录项;
· 更新位图以反映释放的块;
· 释放描述符;
· 返回状态信息。
3.5 打开和关闭文件
文件系统维护了一个打开文件表OFT。它是一个固定长度的数组(声明为文件系统的一部分),其中每一个记录项都由以下条目组成;
· 读/写缓冲区;
· 当前位置;
· 文件描述符索引。
只要打开文件,就会分配一个OFT中的记录项。当关闭文件时,该记录项便被释放。第一个字段是一个被读/写操作使用的缓冲区。缓冲区的大小是一个磁盘块的大小。为了后面的读/写操作,第二个字段包含了在文件内当前字节位置,初值为0。第三个字段是指向磁盘上的文件描述符的索引。
open程序执行的任务如下:
· 搜索目录以便找到文件描述符的索引;
· 分配空闲的OFT记录项(如果可能的话)
· 填写当前位置(0)和文件描述符索引;
· 把文件的第一块读入到缓冲区中(从头读);
· 返回OFT索引。
close程序执行的主要工作有:
· 将缓冲区写到磁盘上;
· 更新描述符中的文件长度;
· 释放OFT记录项;
· 返回状态信息。
3.6 在文件中读、写和搜索
打开文件后,就可以对它进行读和写了。读操作执行如下任务:
(1)计算在读/写缓冲区中的位置,它对应于文件中的当前位置(即文件长度对缓冲区长度取模)。
(2)开始从缓冲区复制字节到指定的内容位置,直到发生下列事件之一。
(a)得到所需数目的字节或者达到文件结尾;在这种情况下,更新当前位置并返回状态信息。
(b)达到缓冲区末尾;在这种情况下,
* 将缓 冲区写入磁盘上的相应块(如果被修改的话);
* 将下一个连续块从磁盘上读到缓冲区中;
* 继续第(2)步。
写入一个文件是类似的:数据从内存传输到缓冲区中,直到满足所需要的字节数或者到达文件结尾。在后一种情况中,缓冲区被写到磁盘上,然后更新文件描述符和位图以反映出新的块,并从缓冲区的开始位置继续写操作。如果文件长度的增加超过了最后一个被分配的块,就必须分配新的块。
lseek操作的任务如下:
· 如果新位置不在当前数据块中,
* 将缓冲区写到磁盘上的相应块中;
* 从磁盘上读取新的数据块到缓冲区中。
· 设置当前位置为新位置;
· 返回状态信息。
3.7 列出目录
该命令执行的任务如下:
· 读目录文件;
· 对于每一个目录项
* 寻找文件描述符;
* 打印文件名和文件长度。
4 演示shell程序
通过开发一组程序执行文件系统提供的各种函数。对于文件的功能进行演示。为了交互式地演示你的
,开发一个演示shell程序(与在进程和资源管理器中的演示shell程序相似),从终端接收命令,调用文件系统相应的函数,并在你的终端上显示结果。例如,create
使用指定的名称创建一个新文件;op 为了读写而打开指定的文件并在屏幕上显示一个索引值;rd 从打开的文件中读入所指定的数目的字节并在屏幕上显示它们。
(1) 开发另外两个支持函数也是很有用的:一个用于在文件中保存数组ldisk[]的内容,另一个用于从该文件中读取内容到数组ldisk[]。这样可以使得模拟的磁盘在登录会话之间保存它的内容。
5 具体任务总结
(1) 设计并实现模拟的输入/输出系统,特别是两个函数read_block(int i, char *p)和write_block(int i, char *p).
(2) 设计并实现在输入/输出系统之上的文件系统。它应该支持定义的用户/文件系统接口的函数。
(3) 为了演示shell程序定义一种命令语言。然后,设计并实现该shell,以便交互式地测试和验证你的文件系统的功能。
(4) 为了研究文件系统行为的各个方面,使用各种不同的命令序列来测试文件系统。
6 附加任务的建议
(1) 扩充文件管理,以允许创建和使用树型结构的子目录。可以使用绝对路径或当前工作目录的概念来完成文件和目录的命名工作。
(2) 为磁盘块实现一种多级索引机制,这样文件的长度就可以超过本项目中假设的最大值(3块)。
(3) 将磁盘用一个4维数组ldisk[C][T][S][B]来实现,其中C是柱面数,T是每个柱面的磁道数,S是每个磁道的扇区(物理块)数,B是每个扇区的字节数。扩展模拟的输入/输出系统,使它同前面一样,可以接受同样的对逻辑块号的请求,但是它会氢这些逻辑块号转换为由柱面、磁道和扇区号组成的实际磁盘地址;它们被用作访问ldisk数组的索引。
用户
文件系统
输入/输出系统
位图
数据块
。。。