一个操作系统的设计与实现——第14章 文件系统

发布时间 2023-11-12 09:56:33作者: 樱雨楼

文件系统是操作系统的一个重要模块。本章将要实现的是文件系统。

14.1 什么是文件系统

文件系统是操作系统用于管理硬盘,并使硬盘更易于使用的模块。

想要管理硬盘,就需要记录硬盘扇区的使用情况,可以使用位图实现这个功能。

想要让硬盘更易于使用,就需要一个非常关键的概念:文件。文件可以将底层的起始扇区号、扇区数等信息用一个文件名代替,存储这些信息的数据结构被称为文件控制块(File Control Block,FCB)。使用文件系统时,可以从文件名出发,找到其对应的FCB,并从中取出起始扇区号、扇区数等信息,提供给更底层的函数使用。

文件系统是一个持久化的系统。对文件系统的操作不能仅仅停留在内存中,而是要及时将其写入硬盘。

文件系统并不是一个很复杂的概念,但由于硬盘与CPU之间的效率相差甚远(约5个数量级),所以,工业级的操作系统使用了大量非常复杂的技术以提高文件系统的效率;很多书籍也使用了非常大的篇幅论述文件系统的实现细节。在我们的操作系统中,不考虑文件系统的效率问题。

14.2 文件系统的实现

请看本章代码14/Util.h

第19~20行,声明了两个字符串辅助函数。

接下来,请看本章代码14/Util.hpp

strEq函数相当于C语言标准库的strcmp函数的简化版,其用于判断两个字符串是否相等。

strCopy函数相当于C语言标准库的strncpy函数,其用于复制字符串。

接下来,请看本章代码14/FS.h

第5~10行, 定义了FCB结构体。FCB中需要存放一个文件的起始扇区号和扇区数,一共8字节;然后,需要决定文件名的最大长度。如果将FCB的大小凑整到16字节,那么文件名的最大长度为7(需要扣除字符串结尾的0),笔者认为这个长度过小,于是,将FCB的大小凑整到32字节,这样一来,文件名的最大长度为23。

第13~17行,声明了文件系统的各种函数。

接下来,请看本章代码14/FS.hpp

第9行,定义了长度为16的FCB列表。一个FCB是32字节,所以一个扇区能存放16个FCB。又因为最后一个FCB被用于魔数(见下文),所以我们的操作系统最多支持15个文件。

第10行,定义了硬盘位图的缓冲区,大小为一个扇区。硬盘位图用于管理扇区的使用情况,其存放在硬盘中,这里定义的是其在内存中的读写缓冲区。一个扇区的位图能够管理4096个扇区,共2M。

第11行,定义了硬盘位图。

fsInit函数用于初始化文件系统。文件系统是一个持久化的模块,仅在硬盘上还不存在文件系统时需要初始化一次。所以,需要使用一个魔数来标识硬盘上是否存在文件系统。在工业级的操作系统中,魔数一般定义在超级块(Super Block)中,超级块是文件系统的核心,其中存放的是对于文件系统来说最重要的信息。但我们的操作系统的超级块中只需要存放一个魔数,所以,实现上没有单独构造超级块,而是将FCB列表中的最后一个FCB作废,并在此位置存储魔数。

我们的操作系统使用了硬盘的前100个扇区,所以,文件系统的FCB列表可以存放在第100个扇区;硬盘位图可以存放在第101个扇区;从第102个扇区开始可用于存放文件。

第15行,从硬盘中读取FCB列表。

第17行,验证魔数。

第19~20行,如果魔数验证通过,就说明文件系统已存在,位于第101个扇区的硬盘位图是有效的,可将其读出,然后初始化hdBitmap。请注意,这里的bitmapInit函数不能将位图缓冲区清零,而是应该沿用硬盘中的数据。

第24~30行用于安装文件系统,其只会运行一次。如果读者想要调试这段代码,可以不断修改魔数的取值,或删除虚拟硬盘并重新创建。

第24~25行,清空FCB列表,然后填入魔数。

第27行,初始化硬盘位图。

第29~30行,将FCB列表和硬盘位图分别写入第100,101个扇区,以实现文件系统的持久化。

fsList函数相当于Linux的ls命令。

第37行,遍历15个FCB。

第39行,判断FCB的起始扇区号是否为0。FCB会被初始化为0,而一个文件的起始扇区号不可能为0(至少为102)。所以,如果一个FCB的起始扇区号为0,就说明这个FCB为空。

第41行,如果FCB不为空,就打印这个FCB的文件名,起始扇区号和扇区数。

__allocateFCB函数用于在FCB列表中找到一个空的FCB,并返回其索引值。如果找不到,则返回-1。

fsCreate函数用于创建文件。在我们的操作系统中,创建文件的过程如下:

  1. 在一个很大的起始扇区号(如1000)处写入一个文件
  2. 调用fsCreate函数,将这个文件安装到文件系统中

第63~68行,尝试分配一个FCB索引值。如果分配失败,则退出此函数。

第70~72行,填写FCB的3个数据成员。

第74~80行,将文件复制到文件系统中。

第82~83行,将FCB列表和硬盘位图分别写入第100,101个扇区,以实现文件系统的持久化。

__findFCB函数用于在FCB列表中查找指定的文件名,并返回FCB的索引值。如果找不到,则返回-1。

fsDelete函数用于删除文件。

第103~108行,尝试查找被删除文件的FCB索引值,如果找不到,则退出此函数。

第110行,将此文件从硬盘位图中删除。

第112行,将此文件从FCB列表中删除。

第114~115行,将FCB列表和硬盘位图分别写入第100,101个扇区,以实现文件系统的持久化。

fsLoad函数用于以文件名作为参数加载3特权级任务。

第121行,尝试查找输入文件的FCB。

第125行,如果找到了输入文件的FCB,则从中取出该文件的起始扇区号和扇区数,然后调用loadTaskPL3函数。

接下来,请看本章代码14/Kernel.c

第16行,调用fsInit函数,完成文件系统的初始化。

14.3 测试

本章代码14/Kernel.c用于测试文件系统的四个函数。