简单文件资料系统的实现.docx
- 文档编号:30730271
- 上传时间:2023-08-19
- 格式:DOCX
- 页数:26
- 大小:42.67KB
简单文件资料系统的实现.docx
《简单文件资料系统的实现.docx》由会员分享,可在线阅读,更多相关《简单文件资料系统的实现.docx(26页珍藏版)》请在冰豆网上搜索。
简单文件资料系统的实现
第三章简单文件系统的实现
3.1设计目的和内容要求
1.设计目的
通过具体的文件存储空间的管理、文件的物理结构、目录结构和文件操作的实现,加深对文件系统内部数据结构、功能以及实现过程的理解。
2.内容要求
(1)在内存中开辟一个虚拟磁盘空间作为文件存储分区,在其上实现一个简单的基于多级目录的单用户单任务系统中的文件系统。
在退出该文件系统的使用时,应将该虚拟文件系统以一个Windows文件的方式保存到磁盘上,以便下次可以再将它恢复到内存的虚拟磁盘空间中。
(2)文件存储空间的分配可采用显式链接分配或其他的办法。
(3)空闲磁盘空间的管理可选择位示图或其他的办法。
如果采用位示图来管理文件存储空间,并采用显式链接分配方式,那么可以将位示图合并到FAT中。
(4)文件目录结构采用多级目录结构。
为了简单起见,可以不使用索引结点,其中的每个目录项应包含文件名、物理地址、长度等信息,还可以通过目录项实现对文件的读和写的保护。
(5)要求提供以下操作命令:
●my_format:
对文件存储器进行格式化,即按照文件系统的结构对虚拟磁盘空间进行布局,并在其上创建根目录以及用于管理文件存储空间等的数据结构。
●my_mkdir:
用于创建子目录。
●my_rmdir:
用于删除子目录。
●my_ls:
用于显示目录中的内容。
●my_cd:
用于更改当前目录。
●my_create:
用于创建文件。
●my_open:
用于打开文件。
●my_close:
用于关闭文件。
●my_write:
用于写文件。
●my_read:
用于读文件。
●my_rm:
用于删除文件。
●my_exitsys:
用于退出文件系统。
3.学时安排
授课2学时,上机9学时。
4.开发平台
C或C++均可。
5.思考
(1)我们的数据结构中的文件物理地址信息是使用C语言的指针类型、还是整型,为什么?
(2)如果引入磁盘索引结点,上述实现过程需要作哪些修改?
(3)如果设计的是一个单用户多任务文件系统,则系统需要进行哪些扩充(尤其要考虑读写指针问题)?
如果设计的是一个多用户文件系统,则又要进行哪些扩充?
3.2预备知识
3.2.1FAT文件系统介绍
1.概述
FAT文件系统是微软公司在其早期的操作系统MS-DOS及Windows9x中采用的文件系统,它被设计用来管理小容量的磁盘空间。
FAT文件系统是以他的文件组织方式——文件分配表(fileallocationtable,FAT)命名的,文件分配表的每个表项中存放某文件的下一个盘块号,而该文件的起始盘块号则保存在它的文件控制块FCB中。
在文件分配表中,一般用FFFF来标识文件的结束;用0000来标识某个逻辑块未被分配,即是空闲块。
为了提高文件系统的可靠性,在逻辑磁盘上通常设置两张文件分配表,它们互为备份。
此外,文件分配表必须存放在逻辑磁盘上的固定位置,而根目录区通常位于FAT2之后,以便操作系统在启动时能够定位所需的文件,其磁盘布局如图3-1所示:
图3-1FAT文件系统磁盘布局
上述磁盘布局中,引导块中主要存放了用于描述分区的各种信息,包括逻辑块的大小、文件分配表的大小及位置、根目录的大小及位置等。
除此之外,用于加载操作系统内核的引导程序也存储在引导块中。
FAT文件系统家族又分为FAT12、FAT16、FAT32三种类型,这里的数字表示文件分配表中每个表项(即簇号)所占的位数,即FAT12中每个表项占1.5个字节(12位),FAT16中每个表项占2个字节(16位),FAT32中每个表项占4个字节(32位)。
由于FAT文件系统是以簇为单位为文件分配磁盘空间的(一个簇是一组连续的扇区,通常包含2n个扇区),因此,FAT32比FAT12和FAT16支持更多的簇数、更小的簇大小和更大的磁盘容量,从而大大提高磁盘空间的利用率。
通常,FAT12适用于小容量磁盘,如软盘;FAT16是MS-DOS的文件系统;FAT32是Windows9x中的主要文件系统,开始支持大容量磁盘。
2.文件控制块FCB
为了正确、方便地操作文件,必须设置相应的数据结构用于存放文件的描述和控制信息,常用的数据结构有文件控制块(简称FCB)和索引节点(简称i节点)。
在FAT文件系统中使用文件控制块。
文件与文件控制块一一对应,而文件控制块的有序集合就称为文件目录,即一个文件控制块就是一个文件目录项。
虽然不同文件系统的文件控制块的内容和格式不完全相同,但通常都包括以下三类信息:
基本信息、存取控制信息和使用信息。
(1)基本信息。
包括文件名、用户名、文件类型、文件的物理地址、文件长度、文件的逻辑结构和物理结构等。
(2)存取控制信息。
一般分别给出文件主、伙伴用户、一般用户的存取权限。
(3)使用信息。
包括文件的建立日期及时间、上次存取文件的日期及时间、当前的使用信息等。
以MS-DOS(使用FAT16文件系统)为例,它的每个文件控制块包括32个字节,其字节分配情况如图3-2所示:
图3-2MS-DOS的文件控制块
其中属性字段占一个字节,它的每一位用来表示该文件是否具有某种属性,如果某一位的值为1,则表示该文件具有该属性。
各位所表示的属性如表3-1所示:
表3-1文件属性对照表
位
7
6
5
4
3
2
1
0
属性
保留
保留
存档
子目录
卷标
系统文件
隐藏
只读
3.根目录区
FAT12、FAT16的根目录区是固定区域、固定大小的,位于第二个FAT之后,如图3-1所示,且占据若干连续扇区,其中FAT12占14个扇区,一共224个根目录项;而FAT16占32个扇区,最多保存512个目录项,作为系统区的一部分。
FAT32的根目录是作为文件处理的,采用与子目录文件相同的管理方式,其位置不是固定的,不过一般情况也是位于第二个FAT之后的,其大小可视需要增加,因此根目录下的文件数目不再受最多512个的限制。
3.2.2几个C语言库函数介绍
由于我们的文件系统是建立在内存的虚拟磁盘上的,在退出文件系统的时候必须以一个文件的形式保存到磁盘上;而在启动文件系统的时候必须从磁盘上将该文件读入到内存的虚拟磁盘中。
下面介绍几个可能会用到的C库函数,在使用这些库函数之前必须包含头文件“stdio.h”。
1.打开文件函数fopen()
(1)格式:
FILE*fopen(constchar*filename,constchar*mode)
(2)功能:
按照指定打开方式打开指定文件。
(3)输入参数说明:
filename:
待打开的文件名,如果不存在就创建该文件。
mode:
文件打开方式,常用的有:
●"r":
为读而打开文本文件(不存在则出错)。
●"w":
为写而打开文本文件(若不存在则创建该文件;反之,则从文件起始位置写,原内容将被覆盖)。
●"a":
为在文件末尾添加数据而打开文本文件。
(若不存在则创建该文件;反之,在原文件末尾追加)。
●"r+":
为读和写而打开文本文件。
(读时,从头开始;在写数据时,新数据只覆盖所占的空间,其后不变)。
●"w+":
首先建立一个新文件,进行写操作,随后可以从头开始读。
(若文件存在,原内容将全部消失)。
●"a+":
功能与"a"相同;只是在文件末尾添加新的数据后,可以从头开始读。
另外,上述模式字符串中都可以加一个“b”字符,如rb、wb、ab、rb+、wb+、ab+等组合,字符“b”表示fopen()函数打开的文件为二进制文件,而非纯文字文件。
(4)输出:
一个指向FILE类型的指针。
2.关闭文件函数fclose()
(1)格式:
intfclose(FILE*stream);
(2)功能:
用来关闭先前fopen()打开的一个文件。
此动作会让缓冲区内的数据写入文件中,并释放系统所提供的文件资源。
(3)输入参数说明:
stream:
指向要关闭文件的指针,它是先前执行fopen()函数的返回值。
(4)输出:
若关闭文件成功则返回0;有错误发生时则返回EOF并把错误代码存到errno。
3.读文件函数fread()
(1)格式:
size_tfread(void*buffer,size_tsize,size_tcount,FILE*stream);
(2)功能:
读二进制文件到内存。
(3)输入参数说明:
buffer:
用于存放输入数据的缓冲区的首地址;
stream:
使用fopen()打开的文件的指针,用于指示要读取的文件;
size:
每个数据块的字节数;
count:
要读入的数据块的个数;
size*count:
表示要求读取的字节数。
(4)输出:
实际读取的数据块的个数。
4.写文件函数fwrite()
(1)格式:
size_tfwite(constvoid*buffer,size_tsize,size_tcount,FILE*stream);
(2)功能:
将数据写到二进制文件中。
(3)输入参数说明:
buffer:
用于存放输出数据的缓冲区的首地址;
stream:
使用fopen()打开的文件的指针,用于指示要写出的文件;
size:
每个数据块的字节数;
count:
要写出的数据块的个数;
size*count:
表示要求写出的字符数。
(4)输出:
实际写出的数据块的个数。
5.判断文件结束函数feof()
(1)格式:
intfeof(FILE*stream)
(2)功能:
用来判断是否已读取到文件末尾。
(3)输入参数说明:
stream:
使用fopen()打开的文件的指针,用于指示要判断的文件。
(4)输出:
如果已读到文件尾则返回非零值,其他情况返回0。
6.定位文件函数fseek()
(1)格式:
intfseek(FILE*stream,longoffset,intorigin);
(2)功能:
移动文件读写指针在文件中的位置。
(3)输入参数说明:
stream:
使用fopen()打开的文件的指针,用于指示要定位读写指针的文件;
offset:
位移量,以字节为单位;
origin:
初始位置,有三个常量:
SEEK_CUR:
读写指针当前位置;
SEEK_SET:
文件开头;
SEEK_END:
文件末尾。
当origin值为SEEK_CUR或SEEK_END时,参数offset可以为负值。
3.3实例系统的设计与实现
本实例系统是仿照FAT16文件系统来设计实现的,但根目录没有采用FAT16的固定位置、固定大小的根目录区,而是以根目录文件的形式来实现的,这也是目前主流文件系统对根目录的处理方式。
3.3.1数据结构设计
1.需要包含的头文件
(1)#include
(2)#include
(3)#include
(4)#include
2.定义的常量
(1)#defineBLOCKSIZE1024磁盘块大小
(2)#defineSIZE1024000虚拟磁盘空间大小
(3)#defineEND65535FAT中的文件结束标志
(4)#defineFREE0FAT中盘块空闲标志
(5)#defineROOTBLOCKNUM2根目录区所占盘块总数
(6)#defineMAXOPENFILE10最多同时打开文件个数
3.数据结构
(1)文件控制块FCB
用于记录文件的描述和控制信息,每个文件设置一个FCB,它也是文件的目录项的内容。
typedefstructFCB//仿照FAT16设置的
{
charfilename[8];//文件名
charexname[3];//文件扩展名
unsignedcharattribute;//文件属性字段:
为简单起见,我们只为文件设置了两种属性:
//值为0时表示目录文件,值为1时表示数据文件
unsignedshorttime;//文件创建时间
unsignedshortdata;//文件创建日期
unsignedshortfirst;//文件起始盘块号
unsignedlonglength;//文件长度(字节数)
charfree;//表示目录项是否为空,若值为0,表示空,值为1,表示已分配
}fcb;
(2)文件分配表FAT
在本实例中,文件分配表有两个作用:
一是记录磁盘上每个文件所占据的磁盘块的块号;二是记录磁盘上哪些块已经分配出去了,哪些块是空闲的,即起到了位示图的作用。
若FAT中某个表项的值为FREE,则表示该表项所对应的磁盘块是空闲的;若某个表项的值为END,则表示所对应的磁盘块是某文件的最后一个磁盘块;若某个表项的值是其他值,则该值表示某文件的下一个磁盘块的块号。
为了提高系统的可靠性,本实例中设置了两张FAT表,它们互为备份,每个FAT占据两个磁盘块。
typedefstructFAT
{
unsignedshortid;
}fat;
(3)用户打开文件表USEROPEN
当打开一个文件时,必须将文件的目录项中的所有内容全部复制到内存中,同时还要记录有关文件操作的动态信息,如读写指针的值等。
在本实例中实现的是一个用于单用户单任务系统的文件系统,为简单起见,我们把用户文件描述符表和内存FCB表合在一起,称为用户打开文件表,表项数目为10,即一个用户最多可同时打开10个文件。
然后用一个数组来描述,则数组下标即某个打开文件的描述符。
另外,我们在用户打开文件表中还设置了一个字段“chardir[80]”,用来记录每个打开文件所在的目录名,以方便用户打开不同目录下具有相同文件名的不同文件。
typedefstructUSEROPEN
{
charfilename[8];//文件名
charexname[3];//文件扩展名
unsignedcharattribute;//文件属性:
值为0时表示目录文件,值为1时表示数据文件
unsignedshorttime;//文件创建时间
unsignedshortdata;//文件创建日期
unsignedshortfirst;//文件起始盘块号
unsignedlonglength;//文件长度(对数据文件是字节数,对目录文件可以是目录项个数)
charfree;//表示目录项是否为空,若值为0,表示空,值为1,表示已分配
//前面内容是文件的FCB中的内容。
//下面设置的dirno和diroff记录了相应打开文件的目录项在父目录文件中的位置,//这样如果该文件的fcb被修改了,则要写回父目录文件时比较方便
intdirno;//相应打开文件的目录项在父目录文件中的盘块号
intdiroff;//相应打开文件的目录项在父目录文件的dirno盘块中的目录项序号
chardir[MAXOPENFILE][80];//相应打开文件所在的目录名,这样方便快速检查出
//指定文件是否已经打开
intcount;//读写指针在文件中的位置
charfcbstate;//是否修改了文件的FCB的内容,如果修改了置为1,否则为0
chartopenfile;//表示该用户打开表项是否为空,若值为0,表示为空,否则表示已
//被某打开文件占据
}useropen;
(4)引导块BLOCK0
在引导块中主要存放逻辑磁盘的相关描述信息,比如磁盘块大小、磁盘块数量、文件分配表、根目录区、数据区在磁盘上的起始位置等。
如果是引导盘,还要存放操作系统的引导信息。
本实例是在内存的虚拟磁盘中创建一个文件系统,因此所包含的内容比较少,只有磁盘块大小、磁盘块数量、数据区开始位置、根目录文件开始位置等。
typedefstructBLOCK0//引导块内容
{
//存储一些描述信息,如磁盘块大小、磁盘块数量、最多打开文件数等、
charinformation[200];
unsignedshortroot;//根目录文件的起始盘块号
unsignedchar*startblock;//虚拟磁盘上数据区开始位置
}block0;
4.全局变量定义
(1)unsignedchar*myvhard:
指向虚拟磁盘的起始地址
(2)useropenopenfilelist[MAXOPENFILE]:
用户打开文件表数组
(3)useropen*ptrcurdir:
指向用户打开文件表中的当前目录所在打开文件表项的位置;
(4)charcurrentdir[80]:
记录当前目录的目录名(包括目录的路径)
(5)unsignedchar*startp:
记录虚拟磁盘上数据区开始位置
5.虚拟磁盘空间布局
由于真正的磁盘操作需要涉及到设备的驱动程序,所以本实例是在内存中申请一块空间作为虚拟磁盘使用,我们的文件系统就建立在这个虚拟磁盘上。
虚拟磁盘一共划分成1000个磁盘块,每个块1024个字节,其布局格式是模仿FAT文件系统设计的,其中引导块占一个盘块,两张FAT各占2个盘块,剩下的空间全部是数据区,在对虚拟磁盘进行格式化的时候,将把数据区第1块(即虚拟磁盘的第6块)分配给根目录文件,如图3-3所示:
图3-3虚拟磁盘空间布局
当然,也可以仿照FAT16文件系统,设置根目录区,其位置紧跟第2张FAT后面,大小也是固定的,这个思路相对要简单一点,请同学们自己去实现。
3.3.2实例主要命令及函数设计
1.系统主函数main()
(1)对应命令:
无
(2)命令调用格式:
无
(3)函数设计格式:
voidmain()
(4)功能:
系统主函数
(5)输入:
无
(6)输出:
无
(7)函数需完成的工作:
①对前面定义的全局变量进行初始化;
②调用startsys()进入文件系统;
③列出文件系统提供的各项功能及命令调用格式;
④显示命令行提示符,等待用户输入命令;
⑤将用户输入的命令保存到一个buf中;
⑥对buf中的内容进行命令解析,并调用相应的函数执行用户键入的命令;
⑦如果命令不是“my_exitsys”,则命令执行完毕后转④。
2.进入文件系统函数startsys()
(1)对应命令:
无
(2)命令调用格式:
无
(3)函数设计格式:
voidstartsys()
(4)功能:
由main()函数调用,进入并初始化我们所建立的文件系统,以供用户使用。
(5)输入:
无
(6)输出:
无。
(7)函数需完成的工作:
①申请虚拟磁盘空间;
②使用c语言的库函数fopen()打开myfsys文件:
若文件存在,则转③;若文件不存在,则创建之,转⑤
③使用c语言的库函数fread()读入myfsys文件内容到用户空间中的一个缓冲区中,并判断其开始的8个字节内容是否为“10101010”(文件系统魔数),如果是,则转④;否则转⑤;
④将上述缓冲区中的内容复制到内存中的虚拟磁盘空间中;转⑦
⑤在屏幕上显示“myfsys文件系统不存在,现在开始创建文件系统”信息,并调用my_format()对①中申请到的虚拟磁盘空间进行格式化操作。
转⑥;
⑥将虚拟磁盘中的内容保存到myfsys文件中;转⑦
⑦使用c语言的库函数fclose()关闭myfsys文件;
⑧初始化用户打开文件表,将表项0分配给根目录文件使用,并填写根目录文件的相关信息,由于根目录没有上级目录,所以表项中的dirno和diroff分别置为5(根目录所在起始块号)和0;并将ptrcurdir指针指向该用户打开文件表项。
⑨将当前目录设置为根目录。
3.磁盘格式化函数my_format()
(1)对应命令:
my_format
(2)命令调用格式:
my_format
(3)函数设计格式:
voidmy_format()
(4)功能:
对虚拟磁盘进行格式化,布局虚拟磁盘,建立根目录文件(或根目录区)。
(5)输入:
无
(6)输出:
无。
(7)函数需完成的工作:
①将虚拟磁盘第一个块作为引导块,开始的8个字节是文件系统的魔数,记为“10101010”;在之后写入文件系统的描述信息,如FAT表大小及位置、根目录大小及位置、盘块大小、盘块数量、数据区开始位置等信息;
②在引导块后建立两张完全一样的FAT表,用于记录文件所占据的磁盘块及管理虚拟磁盘块的分配,每个FAT占据两个磁盘块;对于每个FAT中,前面5个块设置为已分配,后面995个块设置为空闲;
③在第二张FAT后创建根目录文件root,将数据区的第1块(即虚拟磁盘的第6块)分配给根目录文件,在该磁盘上创建两个特殊的目录项:
“.”和“..”,其内容除了文件名不同之外,其他字段完全相同。
4.更改当前目录函数my_cd()
(1)对应命令:
my_cd
(2)命令调用格式:
my_cddirname
(3)函数设计格式:
voidmy_cd(char*dirname)
(4)功能:
改变当前目录到指定的名为dirname的目录。
(5)输入:
dirname:
新的当前目录的目录名;
(6)输出:
无
(7)函数需完成的工作:
①调用my_open()打开指定目录名的父目录文件,并调用do_read()读入该父目录文件内容到内存中;
②在父目录文件中检查新的当前目录名是否存在,如果存在则转③,否则返回,并显示出错信息;
③调用my_close()关闭①中打开的父目录文件;
④调用my_close()关闭原当前目录文件;
⑤如果新的当前目录文件没有打开,则打开该目录文件;并将ptrcurdir指向该打开文件表项;
⑥设置当前目录为该目录。
5.创建子目录函数my_mkdir()
(1)对应命令:
my_mkdir
(2)命令调用格式:
my_mkdirdirname
(3)函数设计格式:
voidmy_mkdir(char*dirname)
(4)功能:
在当前目录下创建名为dirname的子目录。
(5)输入:
dirname:
新建目录的目录名。
(6)输出:
无。
(7)函数需完成的工作:
①调用do_read()读入当前目录文件内容到内存,检查当前目录下新建目录文件是否重名,若重名则返回,并显示错误信息;
②为新建子目录文件分配一个空闲打开文件表项,如果没有空闲表项则返回-1,并显示错误信息;
③检查FAT是否有空闲的盘块,如有则为新建目录文件分配一个盘块,否则释放①中分配的打开文件表项,返回,并显示错误信息;
④在当前目录中为新建目录文件寻找一个空闲的目录项或为其追加一个新的目录项;需修改当前目录文件的长度信息,并将当前目录文件的用户打开文件表项中的fcbstate置为1;
⑤准备好新建目录文件的FCB的内容,文件的属性为目录文件,以覆盖写方式调用do_write()将其填写到对应的空目录项中;
⑥在新建目录文件所分配到的磁盘块中建立两个特殊的目录项“.”和“..”目录项,方法是:
首先在用户空间中准备好内容,然后以截断写或者覆盖写方式调用do_write()将其写到③中分配到的磁盘块中;
⑦返回。
6.删除子目录函数rmdir()
(1)对应命令:
my_r
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 简单 文件 资料 系统 实现