一 实验目的
(1)掌握文件系统的工作原理。
(2)理解文件系统的主要数据结构。
(3)加深理解文件系统的内部功能和实现方法。
二 实验内容
编写程序,设计并实现一个单级单用户的文件系统。可以在内存中开辟一片空间模拟磁盘作为文件系统。文件以块为基本单位,一个块大小为 1024个字节,共 1024 块。文件系统支持的最大文件数为 1024。
2.1 任务一
要求包含以下数据结构:
① 文件分配表 FAT,实现文件的链接分配。表条目指示文件的下一块的块号。
② 文件控制块 FCB,包含文件名、文件大小、文件首块的块号等属性。FCB 存储在根目 录中。
③ 整个系统的打开文件表。显示当前哪些文件已被打开以及被多少个进程打开。
2.2 任务二
要求提供以下操作:
① 文件系统的初始化:包括对 FAT、根目录和打开文件表的初始化。
② 文件创建/删除命令:create/delete。创建文件时指定如文件名、文件大小等信息,并为 其分配存储空间;删除文件时从根目录找到相应项,回收其存储空间和 FCB。
③ 文件打开/关闭命令:open/close。打开文件先检查文件是否已打开,若已打开则计数加 1, 否则从根目录中找到相应项,加入打开文件表中;关闭文件操作类似。
④ 显示目录内容命令:ls:文件列示时要列出文件名和文件大小等信息。
⑤ 进行文件操作时需考虑边界情况:如创建和打开文件时,需要检索是否存在同名文件; 删除文件时,如果文件正在被打开,则不被允许。
2.3 任务三
自行编写测试样例,验证文件系统的可靠性。
三 实验过程及结果
3.1实验代码分析
(1)创建文件
【分析】:新建一个file_system目录,进入目录,在目录下创建demo.c文件编写代码。
(2)头文件引用和宏定义
【分析】:这一部分包括了所需的头文件和文件系统的一些宏定义。头文件包括标准输入输出头文件 (<stdio.h>)、标准库头文件 (<stdlib.h>) 和字符串处理头文件 (<string.h>)。宏定义包括块大小 (BLOCK_SIZE)、块数量 (NUM_BLOCKS)、最大文件数量 (MAX_FILES) 和最大文件名长度 (MAX_FILENAME_LEN)。
(3)数据结构定义
【分析】:在这一部分,定义了三个结构体 (FATEntry、FCB、OpenFileTableEntry) 分别表示文件分配表的条目、文件控制块和打开文件表的条目。此外,定义了三个全局数组来存储文件分配表 (fat)、根目录 (root_directory) 和打开文件表 (open_file_table) 的信息。
(4)文件系统初始化
【分析】:遍历文件分配表,将每个块的 next_block 初始化为 -1;遍历根目录,将文件大小和第一个块的索引初始化为 -1;遍历打开文件表,将条目的状态以及进程使用数初始化为0。
(5)创建文件
【分析】:这个函数用于在文件系统中创建一个新文件。它检查文件大小是否超出文件系统的容量限制,遍历根目录以确保文件名唯一,然后分配块并更新文件分配表和根目录。
(6)删除文件
【分析】:这个函数用于删除文件。首先,它查找根目录以找到要删除的文件,然后检查文件是否正在被打开。如果没有打开的进程,就释放文件占用的块,并更新根目录。
(7)打开文件
【分析】:这个函数用于打开文件。它首先查找根目录以找到要打开的文件,然后更新打开文件表中的状态和打开文件的进程数量。
(8)关闭文件
【分析】:这个函数用于关闭文件。它查找打开文件表以找到要关闭的文件,然后更新打开文件表中的状态和打开文件的进程数量。
(9)列出目录
【分析】:这个函数用于显示当前文件系统中的所有文件。它遍历根目录并打印文件名和大小。
(10)显示打开文件表
【分析】:这个函数用于显示打开文件表的内容。它遍历打开文件表并打印打开的文件名以及打开文件的进程数量。
(11)用户命令交互与主函数
【分析】:首先打印用户命令提示,告诉用户可以执行的操作。然后解析和处理用户输入的命令,根据命令调用相应的文件系统操作函数。main函数是程序的入口点。它初始化文件系统,然后进入一个无限循环,等待用户输入命令。用户可以通过输入命令执行不同的文件系统操作,直到输入"exit"退出程序。
(12)编译执行
【分析】:使用gcc编译demo.c文件,然后运行a.out文件,进入文件系统。
3.2测试用例
(1)创建(create)/删除(delete)文件
【分析】:使用create命令创建一个文件a,给它分配五个块的大小,使用ls命令查看目录列表,表明文件创建成功;使用delete命令删除文件a,使用ls命令查看目录列表,表明文件删除成功
(2)打开(open)/关闭(close)文件
【分析】:使用open命令打开文件a,并打开两次,使用ls_table命令查看打开文件表内容,显示当前文件a被打开,并被两个进程打开;使用close命令关闭文件,关闭两次后,使用ls_table命令查看打开文件表内容,打开文件表为空,表明成功关闭两个打开的文件。
(3)显示目录内容命令(ls)
【分析】:使用ls命令显示目录内容,列出文件名和文件大小。
(4)显示打开文件表内容命令(ls_table)
【分析】:使用ls_table显示打开文件表内容,显示当前哪些文件已被打开以及被多少个进程打开
(5)边界情况(文件打开时不允许删除)
【分析】:文件a,b正在被打开,使用delete命令无法删除打开的文件a,b,只用先关闭文件b,我们才能成功地删除文件b。
(6)边界情况(无法给文件分配超过文件系统大小的块数)
【分析】:文件系统共有1024个块,无法给文件分配超过1024个块,这会超出文件系统的最大容量。
(7)边界情况(创建、打开时,需检索同名文件)
【分析】:因为我们在创建文件时要求无法创建同名文件,这会显示文件已存在,所以在此文件系统中无需考虑同名文件的情况。
(8)退出命令(exit)
【分析】:使用exit命令可以退出文件系统。
四 实验总结
通过这节课,我成功学会了如何设计和实现一个简单的单级单用户文件系统,这一过程可以总结为以下三大关键点:
(1)文件系统核心概念的理解与掌握:首先,我深入学习了文件分配表(FAT)、文件控制块(FCB)以及打开文件表的概念和作用。文件分配表(FAT),实现文件的链接分配。表条目指示文件的下一块的块号。FCB则包含了文件的元数据信息,如文件名、大小和首块的块号等属性。而打开文件表显示当前哪些文件已被打开以及被多少个进程打开。对这些核心概念的理解与掌握,是我们实现对文件的各种操作的核心。
(2)文件管理的学习:其次,我系统地学习了文件系统的初始化过程,包括对FAT、根目录和打开文件表的初始化。通过实践创建、删除、打开和关闭文件的操作,我了解了如何进行文件存储空间的分配和回收,并能够在根目录中准确定位并修改相应的FCB以及打开文件表。这一方面强调了对文件系统结构和内容的有效管理,另一方面则通过打开文件表的管理机制,实现了对文件打开状态的实时追踪与监控。
(3)边界情况处理:最后,我学会了对边界情况处理,例如在创建和打开文件时的同名文件检查,以避免潜在的冲突;在删除文件时,要检查文件的打开状态,删除文件之前要确保文件处于关闭的状态;在创建文件时,分配给文件的块数不能超过文件系统的总块数(容量),对于边界情况的处理提高了系统的鲁棒性和实用性。
通过这次实践,我不仅在理论上深入了解了文件系统的设计和实现原理,而且通过实际操作加深了对这些概念的理解,提高了我的编程技能,也增强了我的问题解决能力。