实验五 磁盘文件操作

更新时间:2023-10-08 19:32:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

实验五 磁盘文件操作

一、实验目的

深入了解磁盘文件系统的实现。

二、实验预备知识

文件的操作;

文件的逻辑结构和物理结构; 磁盘空间的管理; 磁盘目录结构。

三、实验内容

设计一个简单的文件系统,用文件模拟磁盘,用数组模拟缓冲区,要求实现: 支持多级目录结构,支持文件的绝对读路径;

文件的逻辑结构采用流式结构,物理结构采用链接结构中的显示链接方式; 采用文件分配表;

实现的命令包括建立目录、列目录、删除空目录、建立文件、删除文件、显示文件内容、打开文件、读文件、写文件(追加方式)、关闭文件、改变文件属性。

最后编写主函数对所做工作进行测试。

四、提示与讲解

为了正确地实现文件的存取,文件系统设计了一组与存取文件有关的功能模块,用户可以用“访管指令”调用这些功能模块,以实现文件的存取要求。我们把文件系统设计的这一组功能模块称为“文件操作”,实验就是要模拟实现一些文件操作。文件操作不是独立的,它和文件系统的其他部分密切相关,若要实现文件操作就离不开文件的目录结构、文件的组织结构和磁盘空间的管理。因此,这个实验虽然是文件操作的模拟实现,但是还必须模拟一部分文件的组织结构、目录结构和磁盘空间管理的实现。

(1)文件的组织结构

文件的逻辑结构有两种形式:流式文件和记录式文件。实验中只支持流式文件,采用称为显示链接的物理文件结构,把磁盘中每一块的指针部分提出来,组织在一起,形成文件分配表(FAT)。文件分配表的作用不仅如此,其他的作用下面将提到。

磁盘有多少块,文件分配表就有多少项,若某文件的一个磁盘块号为i,则这个文件的下一个磁盘的块号应该记录在文件分配表第i项。例如,某系统文件分配表前几项值如图10所示。某个文件的起始盘块号为3,则该文件的磁盘块号依次为3、4、9、12、13。

第几项 内容 0 -1 1 -1 2 -1 3 4 4 9 5 0 6 7 7 8 8 -1 9 12 10 11 11 -1 12 13 13 -1 14 0 15 0 … 图10 某磁盘文件分配表部分内容

(2)磁盘空间的管理

首先要模拟一个磁盘。因为是实验,不使用真正的磁盘,所以实验中用一个文件模拟一个小磁盘。假设模拟磁盘有128个物理块,每个物理块大小为64字节。盘块的块号从0编起,0,1,2,3??,127。

将前面所讲的文件分配表放在磁盘的开始处,因为盘块有128块,所以文件分配表有

128项,每项占用一个字节。这样文件分配表占用了磁盘的0块和1块,这两块就不能作其他用处。若一个盘块是某个文件的最后一块,填写“-1”表示文件结束。

文件的建立和删除就需要对磁盘的空间进行分配和回收,所以要建立一定的数据表格来记录磁盘的使用情况。用文件分配表的第i项表示第i个盘块的使用情况。磁盘的第0块一定会被系统数据占用,所以任何一个文件的某个盘块块号都不可能是“0”,因而还可以用“0”表示磁盘盘块空闲,若这个盘块已经分配出去,即是某个文件的一块,由上面我们知道文件分配表中对应项记录的是文件下一块的块号或结束标志都不是“0”。这样非“0”值表示盘块已分。像前面图10那张文件分配表中,块号为5、14和15的盘块是空闲的,其余是已分配的。在文件分配表中可以用一个超过盘块编号的正整数表示文件结束,在此实验中采用255代替-1表示文件结束。

如果磁盘中某些部分损坏,只要不是系统区(引导扇区、文件分配表或根目录等),不分配那些坏的盘块,磁盘可以继续使用。在文件分配表对应坏盘块的项不能是“0”,一般也需要特定的数值表示(这个数值应该是盘块编号以外并且不是结束标志的数值,例如,实验中128~254之间的数值)。假设实验中模拟磁盘的第13块和第49块已经损坏,不能使用,则在文件分配表的第13项和第49项写入“254”表示该盘块损坏不能使用。

由于磁盘分配时,有时不能预定文件的大小,例如建立文件时并不知道文件的大小。因而磁盘的分配有时是一块一块申请的。磁盘空间回收时,整个文件删除时回收很多块,但有时文件修改时可能会删除某些内容,造成归还磁盘块,这时是一块一块回收的。这里给出申请一块磁盘空间的流程图,若分配多块时,循环多次即可。分配一个磁盘块时,不应该从文件分配表第一项查起,因为磁盘中最开始的几块为系统数据区(引导扇区、文件分配表、根目录等占用),所以应该从这之后的可分配数据区开始查询。假定系统区域占用了x个盘块,分配一个磁盘块的流程如图11所示。回收一个磁盘块的流程图很简单,比如回收磁盘块的块号为x,只要找到文件分配表中第x项,将第x项的值改为0即可。

开始找到文件分配表第x项,i=xY第i项值是否为0?i=i+1NN第i项是否为最后一项?Y磁盘满,分配失败分配第i块结束 图11 分配一个磁盘块的流程图

(3)目录结构

文件目录是用于检索文件的,它是文件系统实现按名存取的主要手段。文件目录由若干目录项组成,每一个目录记录一个文件的有关信息。一般地说,目录项应该包括如下内容:

① 有关文件的控制信息。例如,用户名、文件名、文件类型、文件属性。实验模拟个人计算机上的文件操作,这部分内容仅包括文件名、文件类型和属性;

② 有关文件结构的信息。例如,文件的逻辑结构、文件的物理结构、记录个数、文件在存储介质的位置等。实验中,仅仅支持流式文件,不支持记录式文件,所以这部分内容仅仅包括文件在存储介质的位置(分给文件第一个盘块的块号,即起始盘块号)、文件的长度;

③ 有关文件管理的信息。例如,文件的建立日期、文件被修改的日期、文件保留期限和记帐信息等。实验中为了简单起见,这部分内容都不采用。

因此,实验中文件的目录项包括:文件名、文件类型、文件属性、文件的起始盘块号、文件的长度,每个目录项占用8个字节,具体结构如下所示:

文件名:3个字节(实验中合法文件名仅可以使用字母、数字和除“$”、“.”和“/”以外的字符,第一个字节的值为“$”时表示该目录为空目录项,文件名和类型名之间用“.”分隔,用“/”作为路径名中目录间分隔符);

文件类型名:2个字节; 文件属性:1字节; 起始盘块号:1个字节;

文件长度:1个字节(为了实验的简单,文件长度单位为盘块)。

有了文件目录后,当用户要求使用某个文件时,文件系统可以顺序查找目录项,并比较文件名,就可以找到指定文件的目录项,根据目录项中有关内容核对使用权限、并读出文件供用户使用。因此文件目录的组织和管理要便于检索和防止冲突。

在操作系统中目录就有根目录和子目录两种目录。除了文件需要登记形成目录外,还要登记子目录的情况。实验中,根目录固定位置、固定大小(可以登记有限个文件或子目录项),子目录像文件一样,可使用任何一个空闲磁盘块。为了实验简单,实验中根目录占用了一个盘块,子目录的长度没有采用可以任意长的方法,而是采用了定长的方法,每个子目录的长度也是一个盘块,只能放8个目录项。文件和目录的登记项是混在一起的,登记项的结构应该和文件目录一样,每个目录项占用8个字节,结构如下:

目录名:3个字节(实验中合法文件名仅可以使用字母、数字和除“$”、“.”、“/”以外的字符,第一个字节的值为“$”时表示该目录为空目录);

未使用2字节(在实验中填写空格); 目录属性:1字节; 起始盘块号:1个字节;

未使用1字节(在实验中填写“0”)。

在目录登记项中,系统为目录名后2个字节(对应文件类型名位置)填写空格,目录起始盘块号后1字节(对应文件长度位置)填写0。目录属性和文件属性占用同一个字节,为了区别目录和文件,该字节每一位代表不同的含义(为“1”表示“是”,为“0”表示“否”),如图12所示,第0位表示文件为只读文件,第1位表示文件为系统文件,第2位表示文件为一般可读、可写的普通文件,第3位表示该登记项不是文件的登记项,而是目录的登记项,其余几位闲置未用。如该字节为8(8=(00001000)2)表示该目录是一个目录的登记项,该字节为3(3=(00000011)2)表示该目录是一个只读系统文件的登记项,该字节为(4=(00000100)2)表示该目录是一个可读可写的普通文件。

第7位 第6位 第5位 第4位 第3位 第2位 第1位 第0位 未使用 未使用 未使用 未使用 目录属性 普通文件 系统文件 只读文件 图12 属性字节各位作用示意图

目录检索的方法常用的是顺序检索,根据绝对路径名查找文件的方法一般如下:先找到根目录的起始盘块,一般根目录位置是固定的,实验中就是模拟磁盘的第2块,将该盘块读出;取出路径名中根目录后的目录名或文件名,和根目录中目录项依次比较,比较完一块,再根据文件分配表找到下一块,再读入比较,直到找到名字一致的目录项或根目录登记项均已查完为止;若没有找到,则查找失败,结束;若查找的是文件,结束;若查找的是目录,从找到的目录项中,取出目录的起始盘块号,读入此盘块,然后用上述相同的查找方法继续查找,直到找到该文件(或目录)或查找失败结束。

实验中,使用绝对路径查找文件(或目录)的流程如图13所示。

查找绝对路径名namei=根目录的起始盘块号;last=0;pna=从name中分离出“/”后下一个目录名(或文件名);若是文件,分离出类型名type读出磁盘第i块,存在buffer2中若pna为目录名或文件名最后一部分,last=1i=该目录起始盘块号是绝对路径的最后一部分且是文件名?Nj=0Yj=0NN第j项是该盘块一项?Y文件名为pna且类型名为type?Y查找成功j=j+1第j项是该盘块一项?j=j+1NY第j项是目录且名字等于pna?Ylast==1?Y查找成功Y结束NN查找失败,该文件或目录不存在 图13 使用绝对路径查找文件(目录)的流程图

查找文件除了绝对路径名外,还可以使用相对路径名。相对路径名是从当前目录出发到指定文件的路径。如果文件(或目录)在当前目录下,使用相对路径名查找速度比较快。和绝对路径的查找方法一样,只是查找的起点是当前目录,不是根目录。实验中只使用了绝对路径名。

(4)文件操作

确定文件组织结构、目录结构和磁盘空间管理的方法后,就可以模拟文件操作的实现。实验中文件操作包括建立文件(create_file)、打开文件(open_file)、关闭文件(close_file)、读文件(read_file)、写文件(write_file)、删除文件(delete_file)、显示文件内容(typefile)和改变文件属性(change),目录命令包括建立目录(md)、显示目录内容(dir)和删除空目录(rd)。在实验中没有程序调用这些指令,为了看到它们的模拟情况,从键盘输入选择指令来模拟用户程序的调用。

首先要建立一个“已打开文件表”,用来记录打开或建立文件的相关内容,结构如图14所示。

文件路径名 文件属性 起始盘块号 文件长度 操作类型 读指针 块号 块内地址 写指针 块号 块内地址 ? ? ? ? ? ? 图14 已打开文件表结构

用数组模拟已打开文件表,数据结构定义如下:

#define n 5 //实验中系统允许打开文件的最大数量 typedef struct { int dnum; //磁盘盘块号 int bnum; //磁盘盘块内第几个字节 }pointer; //已打开文件表中读、写指针的结构 typedef struct {char name[20]; //文件绝对路径名

char attribute; //文件的属性,用1个字节表示,所以采用char类型 int number; //文件起始盘块号 int length; //文件长度,文件占用的字节数 int flag; //操作类型,用“0”表示以读操作方式开文件,用“1”表示写操作方式打开文件

pointer read; //读文件的位置,文件打开时dnum为文件起始盘块号,bnum为“0” pointer write; //写文件的位置,文件刚建立时dnum为文件起始盘块号,bnum为“0”,打开文件时dnum和bnum为文件的末尾位置 }OFILE; //已打开文件表项类型定义 struct

{OFILE file[n]; //已打开文件登记表 int length; //已打开文件登记表中登记的文件数量 }openfile; //已打开文件登记表定义

无论上述哪种文件操作都会涉及到已打开文件表,对于已打开文件表主要是查找、删除和插入操作。下面给出这三种操作的流程图,在已打开文件表中查找某文件是否存在的流程如图15所示,将某文件从已打开文件表中删除的流程如图16所示,将某个文件登记在已打开文件表的流程图如图17所示。

删除路径名为name的文件 查找路径名为 pname的文件i=0在已打开文件表中查找路径名为name的登记项ii为已打开文件表一栏?i=i+1NY文件路径名相符?Y查找成功N找到该文件登记项i?N查找失败Y删除第i项:openfile.file[i]=openfile.file[openfile.length-1]openfile.length=openfile.length-1;文件没有打开,删除失败结束

结束 图15 在已打开文件表中查找某文件的流程图 图16 将某文件从已打开文件表中删除的流程图

插入路径名为name的文件在已打开文件表中查找路径名为name的登记项i找到该文件登记项i?NN已打开文件表已满?YY文件已经打开在openfile.file[length]处填写该文件的各项内容;openfile.length=openfile.length+1文件登记表满,无法打开文件结束 图17 将某文件插入已打开文件表的流程图

(1)建立文件(create_file)

用户要把一个新文件放到存储介质上前,首先调用文件系统的“建立”操作。

“建立文件”的主要工作就是检查文件目录,确认无重名文件后,寻找空闲登记项进行登记;寻找空闲存储块(至少一块)以备存储文件信息或存放索引表,最后应该填写已打开文件表。

实验中需要的参数比较少,只要有文件名、文件属性就可以,create_file(文件名,文件属性)。 实验中,建立文件时给出文件名和文件属性,文件属性如果是只读性质则不能建立;文件建立时根据给定的文件路径名进行查找,如果父目录不存在,建立文件失败;如果存在,查看有无重名文件,如果有,则提示该文件已存在,建立文件失败;如无重名文件,则为该文件建立文件目录,并分配给它一个磁盘块;最后填写目录和已打开文件表。建立文件的流程如图18所示。

路径名name,属性attribute是只读文件?NY不能建立文件,建立文件失败已打开文件表满?Y不能建立只读文件,建立文件失败结束N将name分离成建立文件的目录路径名pathname和文件名dname和文件类型名type查找pathname结束找到该目录?Y d=该目录项起始盘块号 第d块内容读入buffer2b=-1;t=0查buffer2中第t个目录项N指定的文件目录不存在,建立文件失败结束第t项是文件且名字是fname、类型名是tmane?t=t+1N第t项是空栏并且b==-1?NN第t项是该盘块最后一项?YY无空目录?N申请一个磁盘块dd申请磁盘块成功?N磁盘无空间,建立文件失败结束YYY指定的文件已经存在,建立文件失败b=t;结束无目录项,建立文件失败填目录和已打开文件表 图18 模拟建立文件的流程图

(2)打开文件(open_file)

用户要求使用一个已经存在的文件时,首先执行“打开文件”操作。 实验中,所需参数有文件名、操作类型(读或写),open_file(文件名,操作类型)。 实验中,打开文件首先要检查该文件是否存在,不存在,打开失败;如果文件存在,还要检查打开方式,确保不能以写方式打开只读文件;最后填写已打开文件表,若文件已经打开则不需要填写已打开文件表,打开文件的流程如图19所示。

打开路径名为pname的文件查找该文件的目录项找到该文件目录项?YN操作不合法,无法打开文件文件属性与操作类型相符?Y在已打开文件表中插入该文件登记项结束N文件不存在,无法打开文件 图19 打开文件的流程图

(3)读文件(read_file)

用户要求读文件信息时调用文件系统的“读文件”操作。

实验中,读文件的参数只需要文件名和读取长度,read_file(文件名,读取长度)。因为采用的是流式文件结构,所以读的长度用字节表示。

实验中,读文件操作的主要工作是查找已打开文件表中是否存在该文件;如果不存在,不能读;然后检查是否是以读方式打开文件,如果是以写方式打开文件,则不允许读;最后从已打开文件表中读出读指针,从这个位置上读出所需要长度,若所需长度没有读完已经遇到文件结束符,就终止读操作。实验中用“#”表示文件结束,读文件的流程如图20所示。

(4)写文件(write_file)

用户要求存取文件信息时调用文件系统的“写文件”操作。实验中,写文件的参数只需要文件名、存放准备写入磁盘信息的缓冲和写的长度,write_file(文件名,缓冲,写长度)。因为采用的是流式文件结构,所以写长度用字节表示。

实验中,写文件操作的主要工作是查找已打开文件表中是否存在该文件,如果不存在,不能写;如果存在,还要检查是否以写方式打开文件;如果不是,写方式打开文件,不能写;最后从已打开文件表中读出写指针,从这个位置上写入缓冲中的数据。

读name文件length字节查找已打开文件表N文件未打开,无法读文件结束N从已打开文件表得到读指针; 将盘块dum读入缓冲buffer1文件打开?Y文件以读方式打开?Y文件不能读结束t=0;文件未结束且t<=length?Y显示读出内容;修改读指针的bunum:bnum=bnum+1;t=t+1N结束N读文件结束读完一个盘块?Y修改读指针:bnum=0;dnum=文件分配表第dnum项将盘块dnum读入缓冲buffer1 图20 读文件的流程图

写文件有两种情况,一种情况是建立文件后的写入,这种写比较简单,一边写一边申请空间即可完成;一种情况是文件打开后的写入,这个比较复杂,存在着文件中间修改的问题。实验中,第二种情况只完成了从文件末尾向后追加的功能,写文件的流程如图21所示。

将buff内容写入name文件length字节查找已打开文件表N文件未打开,无法写文件结束文件不能写结束Y从已打开文件表得到写指针:盘块号dnum,字节位置bnum文件打开?YN文件以写方式打开?t=0;t

(5)关闭文件(close_file)

用户对文件读写完毕后需要调用文件系统的“关闭文件”操作。 实验中,关闭文件的参数只需要文件名,close_file(文件名)。

实验中关闭文件,首先要看该文件是否打开,如果没有打开,就不用关闭;如果已经打开,则检查打开方式,如果是写方式打开的,要追加文件结束符,修改目录项;最后从已打开文件表中删除对应项,关闭文件的流程如图22所示。

关闭路径名为name的文件查找已打开文件表N文件未打开,无法关闭文件结束文件打开?YN操作为“写”?Y追加文件结束符修改目录中文件长度在已打开文件表中删除该文件登记项结束

图22 模拟关闭文件的流程图

(6)删除文件(delete_file)

用户认为文件没有必要保存时需要调用文件系统的“删除文件”操作。实验中,删除文件时参数只要文件名,delete_file(文件名)。

实验中,删除文件操作的主要工作是检查文件是否存在;不存在,操作失败;如存在,查找该文件是否打开,如果打开不能删除;如果没有打开,则删除文件目录项并归还文件所占磁盘空间,删除文件的流程如图23所示。

(7)显示文件内容(typefile)

显示文件内容首先要找到该文件的目录登记项,如果文件不存在,指令执行失败;如果存在,查看文件是否打开,打开则不能显示文件内容;若没有打开,从目录中取出文件的起始盘块号,一块一块显示文件内容,显示文件内容的流程如图24所示。

删除路径名为name的文件 查找该文件的目录项 N 该文件存在? Y 文件不存在,无法查找该已打开文件表 删除文件 N 找到该文件? 结束 Y t=文件起始盘块号 文件正在使用,不能删除 结束 删除该文件目录

显示路径名为name的文件查找目录nameN指定的文件不存在,显示文件内容失败结束找到该文件?YY文件打开?Ndnum=该目录起始盘块号文件打开,显示文件失败结束第dnum块是该文件一块?Y第dnum块内容读入buffer1显示buffer1中的内容结束或到文件结束符dnum=FAT第dnum项N结束(8)改变文件属性(change)

改变文件属性,首先查找该文件,如果不存在,结束;如果存在,检查文件是否打开,打开不能改变属性;没有打开,根据要求改变目录项中属性值。

实验中,首先要系统初始化,包括建立文件c模拟磁盘、初始化磁盘FAT、初始化根目录为空目录项;然后,可以选择一项功能执行。

目录的操作命令: ① 建立目录(md)

建立目录首先要找到建立目录的位置(父目录),然后查找该目录是否存在,如果父目录不存在,不能建立;如果存在,查找是否存在同名目录,存在,不能建立;不存在,则查找一个空目录项,为该目录申请一个盘块,并填写目录内容。

建立目录流程图如图25所示。 ② 显示目录内容(dir)

显示目录内容首先要找到该目录,如果目录不存在,指令执行失败;如果存在,一项一项显示目录内容。显示目录内容的流程如图26所示。

③ 删除空目录(rd)

删除空目录首先要找到该目录,如果目录不存在,指令执行失败;如果存在,但是根目录或非空目录,显示不能删除,操作失败;若是非空子目录,则删除其目录项并回收对应空

图24 显示文件的流程图

间。删除空目录的过程和删除文件的过程相似,流程可参考文件的删除过程。

另外注意,对磁盘文件进行读操作时,需要磁盘的一个盘块读入主存后才能进行处理,对磁盘文件进行写操作时,要写满缓冲后才写入磁盘。所以模拟文件操作时,不能将整个模拟磁盘的内容同时读入主存,应该当需要模拟磁盘的某个盘块内容时,从对应文件中读出;修改后需要写回模拟磁盘。实验中就是用这种方法模拟磁盘的输入输出。

实验中定义了两个数组buffer1和buffer2模拟缓冲。

实验中首先系统初始化,包括建立文件c模拟磁盘、初始化磁盘FAT和根目录初始为空目录项,然后选择各个命令进行测试。

五、课外题

在上述基础上,将磁盘文件系统改进为支持多级树型目录,支持相对路径,子目录可以任意长的文件系统。再添加文件的拷贝、移动指令和非空目录的删除指令。

六、参考程序

#define false 0 #define true 1

#include \typedef struct { char name[3]; char type[2]; char attribute; char address; char length;

//文件或目录名 //文件类型名 //属性

//文件或目录的起始盘块号 //文件长度,以盘块为单位

路径名为name将name分离成建立文件的目录路径名pathname和目录名dname查找pathnameN指定目录不存在,建立目录失败结束b=-1;t=0;查buffer2中第t个目录项找到该目录?Y目录项起始盘块号d第d块内容读入buffer1第t项是目录且名字是dname?Nt=t+1第t项是空栏且b==-1?Yb=t;NY指定的目录已经存在,建立目录失败结束N第t项是该盘块最后一项?YN无目录项,建立失败结束目录中有空项b(b≠-1)?Y申请一个磁盘块ddN申请磁盘块成功?Y初始化该块所有目录项为空目录项无目录项,建立失败在第d块第b项建立该目录的目录:目录名fname、属性、起始盘块号dd、长度0;FAT表第dd项结束标志。结束

//目录结构

//模拟实验中系统允许打开文件的最大数量 //磁盘盘块号

//磁盘盘块内第几个字节

//已打开文件表中读写指针的结构

图25 建立目录的流程图

}content; #define n 5 typedef struct { int dnum; int bnum; }pointer;

显示路径名为name的目录查找目录nameN指定目录不存在,显示目录内容失败结束 t=0;查buffer2中第t个目录项Y找到该目录?Y该目录项起始盘块号dnum;第dnum块内容读入buffer2第t项是空目录项?t=t+1N显示该目录内容N第t项是该盘块最后一项?Y结束 图26 显示目录内容的流程图

typedef struct {char name[20]; //文件绝对路径名 char attribute; //文件的属性,用1个字节表示,char类型 int number; //文件起始盘块号 int length; //文件长度,文件占用的字节数int flag; //操作类型,用“0”表示以读操作方式开文件的,用“1”表示写操作方式打开文件的 pointer read; //读文件的位置,文件刚打开时dnum为文件起始盘 pointer write; //写文件的位置,文件刚建立时dnum为文件起始盘 块号,bnum为“0”,文件打开时为文件的末尾 }OFILE; //已打开文件表项类型定义 struct

{OFILE file[n]; //已打开文件表 int length; //已打开文件表中登记的文件数量 }openfile; //已打开文件表定义 char buffer1[64]; //模拟缓冲1 content buffer2[8]; //模拟缓冲2 FILE *fc; //模拟磁盘的文件的指针

copen(x1,x2)

OFILE *x1,*x2; {

strcpy(x1->name,x2->name); x1->attribute=x2->attribute; x1->number=x2->number; x1->length=x2->length; x1->flag=x2->flag;

x1->read.dnum=x2->read.dnum; x1->read.bnum=x2->read.bnum; x1->write.dnum=x2->write.dnum; x1->write.bnum=x2->write.bnum; }

sopen(name)

//在已打开文件表中查找文件name char *name; {int i; i=0;

//依次查找已打开文件表

while(i=openfile.length) return(-1); return(i);

}//查找函数sopen结束

dopen(name)

//在已打开文件表中删除文件name char *name; {int i;

i=sopen(name); if(i==-1)

printf(\文件未打开\\n\ else

{copen(&openfile.file[i],&openfile.file[openfile.length-1]); openfile.length--; }

}//删除函数dopen结束

iopen(x)

//在已打开文件表中插入n文件x->ame content *x; {int i;

i=sopen(x->name); if(i!=-1)

{printf(\文件已经打开\\n\ return(false); }

else if(openfile.length==n) { printf(\已打开文件表已满\\n\ return(false); } else

{copen(&openfile.file[openfile.length],x); openfile.length++; return(true); }

}//填写已打开文件表函数结束

allocate( )

//分配一个磁盘块,返回块号 {int i;

fseek(fc,0,SEEK_SET); //将模拟磁盘的文件指针移至模拟磁盘FAT表 fread(buffer1,64L,1,fc); //将FAT表中第一个盘块读入模拟缓冲buffer1中 for(i=3;i<63;i++) if(buffer1[i]==0)

//FAT中的第i项为0,分配第i块磁盘块,修改FAT表,并且写回磁盘

{ buffer1[i]=255;

fseek(fc,0,SEEK_SET);

fwrite (buffer1,64L,1,fc); return(i); //返回磁盘号 }

fread(buffer1,64L,1,fc); //将FAT表中第二个盘块读入模拟缓冲buffer1中 for(i=0;i<63;i++)

if(buffer1[i]==0) //FAT中的第i项为0,分配第i+64块磁盘块,修改FAT表,并且写回磁盘

{ buffer1[i]=255;

fseek(fc,-64L,SEEK_CUR); fwrite(buffer1,64L,1,fc); return(i+64); //返回磁盘号 }

printf(\已经没有磁盘空间\\n\ return(false);

}//分配磁盘块函数结束

read_file(name,length)

//读文件函数,文件路径名name,读取长度length char *name; int length; {int i,t; char ch;

if((i=sopen(name))==-1)

{printf(\文件没有打开或不存在\\n\ return(false); }

if(openfile.file[i].flag==1)

{printf(\文件以写方式打开,不能读\\n\ return(false); } t=0;

fseek(fc,openfile.file[i].read.dnum*64L,SEEK_SET); fread(buffer1,64,1,fc);

while(t

openfile.file[i].read.bnum++;

if(openfile.file[i].read.bnum>=64) //一块读完,读取下一个盘块 {fseek(fc,openfile.file[i].read.dnum/64*64, SEEK_SET); fread(buffer1,64,1,fc);

openfile.file[i].read.dnum=buffer1[openfile.file[i].read.dnumd]; //修改读指针

openfile.file[i].read.bnum=0;

fseek(fc,openfile.file[i].read.dnum*64L,SEEK_SET); fread(buffer1,64,1,fc); //读取下一个 } t++; }

}//读函数结束

write_file(name,buff,length) //写文件函数 char *name; //文件路径名 char *buff; //存放准备写入磁盘的内容 int length; //写入内容的长度 {int i,t,dd;

if((i=sopen(name))==-1) //文件不存在,无法写 {printf(\文件没有打开或不存在\\n\ return(false); }

if(openfile.file[i].flag==0)

{printf(\文件以读方式打开,不能写\\n\ return(false);

} t=0;

fseek(fc,openfile.file[i].write.dnum*64L, SEEK_SET); fread(buffer1,64,1,fc); while(t

{buffer1[openfile.file[i].write.bnum]=buff[t]; openfile.file[i].write.bnum++; openfile.file[i].length++;

if(openfile.file[i].write.bnum>=64)

{fseek(fc, openfile.file[i].write.dnum*64L, SEEK_SET); fwrite(buffer1,64,1,fc); //一块写完,写回磁盘 if((dd=allocate())==false)

{ openfile.file[i].write.bnum--;

printf(\无磁盘空间,部分信息丢失,写失败\\n\ return(false); }//if

fseek(fc,openfile.file[i].write.dnum/64*64L, SEEK_SET); fread(buffer1,64,1,fc);

buffer1[openfile.file[i].write.dnumd]=dd;

fseek(fc,openfile.file[i].write.dnum/64*64L, SEEK_SET); fwrite(buffer1,64,1,fc);

openfile.file[i].write.dnum=dd; openfile.file[i].write.bnum=0; }//if t++; }//while

fseek(fc, openfile.file[i].write.dnum*64L, SEEK_SET); fwrite(buffer1,64,1,fc); //一块写完,写回磁盘 }//写函数结束

search(name,flag,dnum,bnum)

//查找路径名为name的文件或目录,返回该目录的起始盘块号 char *name; int flag; //flag=8表示查找目录,否则为文件 int *dnum,*bnum; //返回文件或目录的目录项登记位置,盘块dnum中第bnum项

{int k,i,s,t,j,last=0; char pna[3],type[2];

if((strcmp(name,\根目录 return(2); k=0;

if(name[0]=='/')k=1; i=2; // i=根目录的起始盘块号 while(last!=1)

{// pna=从name中分离出\后一个目录名(或文件名)

for(s=0;name[k]!='.'&&name[k]!='/'&&s<3&&name[k]!='\\0';s++,k++) pna[s]=name[k]; for(;s<3;s++) //用空格补全名字长度 pna[s]=' ';

while(name[k]!='.'&&name[k]!='\\0'&&name[k]!='/')//除去多余字符 k++;

type[0]=type[1]=' '; if(name[k]=='.') //取文件类型名type if(flag==8)

{printf(\目录不应该有有类型名,查找失败\\n\ return(false); } else

{//文件遇到类型名认为结束,后面的字符作废 k++;

if(name[k]!='\\0')type[0]=name[k]; k++;

if(name[k]!='\\0')type[1]=name[k]; if(name[k]!='\\0'&&name[k+1]!='\\0') { printf(\文件名错误\\n\ return(false); }

last=1; } else

if(name[k]!='\\0')k++; if(name[k]=='\\0') last=1;

//查找名字等于pna的目录项 fseek(fc,i*64L,SEEK_SET); fread(buffer2,64L,1,fc); j=0;

if(last==1&&flag!=8) //查找名字pna、类型名type的文件目录项 while(j<8&&!(buffer2[j].attribute!=8&&buffer2[j].name[0]==pna[0]&& buffer2[j].name[1]==pna[1]&&buffer2[j].name[2]==pna[2]&& buffer2[j].type[0]==type[0]&&buffer2[j].type[1]==type[1])) j++; else //查找名字等于pna的目录项

while(j<8&&!(buffer2[j].attribute==8&&buffer2[j].name[0]==pna[0]&&buffer2[j].name[1]==pna[1]&&buffer2[j].name[2]==pna[2])) j++; if(j<8) //找到该目录或文件 if(last==1) //查找结束 {*dnum=i; *bnum=j;

return(buffer2[j].address); }

else //查找还未结束 i=buffer2[j].address; //读取下一个盘块 else

{ printf(\路径错误\\n\ return(false); }

}//while 查找结束 }//search()结束

create_file(name,attribute)

//建立文件函数,路径名name,文件属性attribute char *name; int attribute;

{int dnum,bnum,i,j,last,k,s,d,t,tt,b,dd,flag,dn,bn; char dname[3],tname[2],pathname[20]; OFILE x;

if(attribute%2==1)

{printf(\只读文件,无法写,不能建立\\n\ return(false); }

if(openfile.length==n)

{printf(\已打开表已满,不能建立\\n\ return(false); }

//将name分成两部分,目录路径pathname和目录名dname for(j=0;name[j]!='\\0';j++) //查找最后一个“/” if(name[j]=='/')s=j;

//分离目录路径

for(j=0;j

pathname[j]=name[j]; pathname[j]='\\0'; //分离文件名

for(k=0,j=s+1;name[j]!='\\0'&&k<3&&name[j]!='.';j++,k++) dname[k]=name[j]; if(k==0)

{printf(\错误文件名或目录名\\n\ return(false); }

for(;k<3;k++) dname[k]=' '; k=0;

if(name[j++]=='.') //分离类型名 { for(;name[j]!='\\0'&&k<2&&name[j]!='.';j++,k++) tname[k]=name[j]; }

for(;k<2;k++) tname[k]=' ';

if((d=search(pathname,8,&dn,&bn))==false) //找到目录路径 {printf(\目录不存在,不能建立\ return(false); }

//确认该目录不存在的同时查找空目录项 b=-1;

fseek(fc,d*64L,SEEK_SET); fread(buffer2,64L,1,fc); //读出d盘块的内容 for(t=0;t<8;t++)

{if(buffer2[t].name[0]==dname[0]&&buffer2[t].name[1]==dname[1]&&buffer2[t].name[2]==dname[2]&&buffer2[t].type[0]==tname[0]&&buffer2[t].type[1]==tname[1])

{//找到名字dname的文件,建立失败 printf(\文件已经存在,不能建立\\n\ return(false); }

if(buffer2[t].name[0]=='$'&&b==-1) b=t; }//for if(b==-1) //没有空目录项,建立失败 { printf(\目录无空间\\n\ return(false); }

if((dd=allocate( ))==false) //分配给建立目录的磁盘盘块dd { printf(\建立文件失败\\n\ return(false); }

//填写目录项

for(i=0;i<3;i++)

buffer2[b].name[i]=dname[i]; for(i=0;i<2;i++)

buffer2[b].type[i]=tname[i]; buffer2[b].attribute=attribute; buffer2[b].address=dd; buffer2[b].length=0;

fseek(fc,d*64L,SEEK_SET); fwrite(buffer2,64L,1,fc); //填写已打开文件表

strcpy(x.name,name);

printf(\写文件\\n\ printf(\关闭文件\\n\ printf(\删除文件\\n\ printf(\建立目录\\n\

printf(\显示目录内容\\n\ printf(\– 删除目录\\n\

printf(\显示文件内容\\n\ printf(\改变文件属性\\n\ printf(\选择功能项(0~9):\ scanf(\ switch(a) {

case 0: //a=0程序结束 fclose(fc); exit(0); case 1: //a=1建立文件

printf(\输入文件路径名和文件属性(1-只读文件,3-只读系统文件,4-普通文件):\

scanf(\

create_file(name,attribute); //建立文件 break; case 2: //a=2打开文件

printf(\输入文件路径名和操作类型(0-读文件,1-写文件):\ scanf(\ open_file(name,type); //打开文件 break; case 3: //a=3读文件 printf(\输入文件路径名和读长度\ scanf(\ read_file(name,length); //读文件 break; case 4: //a=4写文件 printf(\输入文件路径名:\ scanf(\

printf(\输入写的内容和和写长度\ scanf(\ write_file(name,buffer,length); //写文件 break; case 5: //a=5关闭文件 printf(\输入文件路径名\ scanf(\

close_file(name); //关闭文件 break; case 6: //a=6删除文件 printf(\输入文件路径名\ scanf(\ delete(name); //删除文件 break; case 7: //a=7建立目录 printf(\输入目录路径名\ scanf(\ md(name); //建立目录 break; case 8: //a=8显示目录 printf(\输入目录路径名\ scanf(\ dir(name); //显示目录 break; case 9: //a=9删除目录 printf(\输入目录路径名\

scanf(\ rd(name); //删除目录 break; case 10: //a=10显示文件 printf(\输入文件路径名\ scanf(\ typefile(name); //显示文件 break; case 11: // a=11改变文件属性

printf(\输入文件路径名和文件属性(1-只读文件,3-只读系统文件,4-普通文件):\

scanf(\ change(name,attribute); }// switch }// while }//main( )结束

x.attribute=attribute; x.number=dd; x.length=0; x.flag=1;

x.read.dnum=x.write.dnum=dd; x.read.bnum=x.write.bnum=0; iopen(&x); }//建立文件结束

open_file(name,attribute) //打开文件函数 char *name;

int attribute; {OFILE x;

int dnum,bnum,last,i,d;

if((d=search(name,4,&dnum,&bnum))==false) {printf(\文件不存在,打开操作失败\\n\ return(false); }

fseek(fc,dnum*64L,SEEK_SET); //读出对应目录项 fread(buffer2,64,1,fc);

if((buffer2[bnum].attribute%2==1)&& attribute==1) //对只读文件要求写 { printf(\文件不能写,打开失败\ return(false); }

strcpy(x.name,name);

x.attribute=buffer2[bnum].attribute; x.number=buffer2[bnum].address;

x.read.dnum=x.write.dnum=buffer2[bnum].address; x.read.bnum=x.write.bnum=0; x.flag=attribute; if(attribute==1)

{while(d!='\\xff') //寻找文件末尾 {fseek(fc, d/64*64L, SEEK_SET); fread(buffer1,64L,1,fc); //读出dnum项所在FAT last=d;

d=buffer1[dd]; //读出dnum块下一块内容赋给dnum }//while

x.write.dnum=last; //填写写指针 fseek(fc, last*64L, SEEK_SET); fread(buffer1,64L,1,fc);

for(i=0;i<64&&buffer1[i]!='#';i++); x.write.bnum=i;

x.length=(buffer2[bnum].length-1)*64+i; }

iopen(&x); //填写已打开文件表 }

close_file(name) //关闭文件函数 char *name;

{int i,dnum,bnum;

if((i=sopen(name))==-1)

{printf(\打开的文件中没有该文件,关闭失败\\n\ return(false); }

if(openfile.file[i].flag==1) //写文件的追加文件结束符 {fseek(fc,openfile.file[i].write.dnum*64L, SEEK_SET); fread(buffer1,64,1,fc);

buffer1[openfile.file[i].write.bnum]='#';

fseek(fc,openfile.file[i].write.dnum*64L, SEEK_SET); fwrite(buffer1,64,1,fc); fputc('#',fc);

search(name,4,&dnum,&bnum); //查找该文件目录位置 //修改目录中的文件长度

fseek(fc,dnum*64L, SEEK_SET); fread(buffer2,64,1,fc);

buffer2[bnum].length=openfile.file[i].length/64+1; fseek(fc, dnum*64L, SEEK_SET); fwrite(buffer2,64,1,fc); }

//在已打开文件表中删除该文件的登记项 if(openfile.length>1)

copen(&openfile.file[i],&openfile.file[openfile.length-1]); openfile.length--; }

delete(name) //删除文件 char *name; {

int dnum,bnum,t;

if((t=search(name,4,&dnum,&bnum))==false)//查找文件 {printf(\文件不存在\\n\ return(false); }

if(sopen(name)!=-1) //文件打开,不能删除 { printf(\该文件打开,不能删除\\n\ return(false); }

fseek(fc,dnum*64L, SEEK_SET); fread(buffer2,64,1,fc); buffer2[bnum].name[0]='$'; //将该文件的目录置成空目录 fseek(fc,dnum*64L, SEEK_SET); fwrite(buffer2,64,1,fc); while(t!='\\xff') //通过FAT查找每一个盘块号,并依次删除 {dnum=t;

fseek(fc, dnum/64*64, SEEK_SET); fread(buffer1,64,1,fc); t=buffer1[dnumd]; buffer1[dnumd]=0;

fseek(fc, dnum/64*64L, SEEK_SET); fwrite(buffer1,64,1,fc); }

}//文件删除结束

md(name)

//建立目录函数,目录路径名name char *name;

{int dnum,bnum,i,j,last,k,s,d,t,tt,b,dd,flag,dn,bn; char dname[3],pathname[20]; i=2; // i=根目录的起始盘块号 //将name分成两部分,目录路径pathname和目录名dname for(j=0;name[j]!='\\0';j++) //查找最后一个“/” if(name[j]=='/')s=j; //分离目录路径

for(j=0;j

pathname[j]=name[j]; pathname[j]='\\0'; //分离目录名

for(k=0,j=s+1;name[j]!='\\0'&&k<3&&name[j]!='.';j++,k++) dname[k]=name[j]; if(k==0)

{printf(\错误文件名或目录名\\n\ return(false); }

for(;k<3;k++) dname[k]=' ';

if((d=search(pathname,8,&dn,&bn))==false) //找到目录路径 {printf(\目录不存在,不能建立\\n\ return(false); }

b=-1;

//确认该目录不存在的同时查找空目录项 fseek(fc,d*64L,SEEK_SET); fread(buffer2,64L,1,fc); //读出d盘块的内容 for(t=0;t<8;t++)

{ if(buffer2[t].name[0]==dname[0]&&buffer2[t].name[1]==dname[1] &&buffer2[t].name[2]==dname[2]&&buffer2[t].attribute==8) {//找到名字dname的目录,建立失败 printf(\目录已经存在,不能建立\\n\ return(false); }

if(buffer2[t].name[0]=='$'&&b==-1) b=t; }//for if(b==-1) //没有空目录项,不能建立 { printf(\目录无空间\\n\ return(false); }

if((dd=allocate( ))==false) //分配给建立目录的磁盘盘块dd { printf(\没有磁盘空间\\n\ return(false); }

//填写目录项

for(i=0;i<3;i++)

buffer2[b].name[i]=dname[i];

buffer2[b].type[0]=buffer2[b].type[1]=' '; buffer2[b].attribute=8; buffer2[b].address=dd; buffer2[b].length=0;

fseek(fc,d*64L,SEEK_SET); fwrite(buffer2,64L,1,fc); //分给新建目录的盘块初始化 for(t=0;t<8;t++)

buffer2[t].name[0]='$';

fseek(fc, dd*64L, SEEK_SET); fwrite(buffer2,64L,1,fc); }//建立目录结束

dir(name) //显示目录内容 char *name;

{int i,bnum,t,tt,dnum,dn,bn;

if((dnum=search(name,8,&dn,&bn))==false) //找到目录路径 { printf(\目录不存在\\n\ return(false); }

printf(\名称 扩展名 起始盘块 长度\\n\ //显示目录内容

fseek(fc,dnum*64L, SEEK_SET); fread(buffer2,64L,1,fc); for(t=0;t<8;t++) //显示该盘块中目录项的内容 if(buffer2[t].name[0]!='$')

printf(\%c%c%c %c%c M}\\n\buffer2[t].name[0], buffer2[t].name[1],buffer2[t].name[2],buffer2[t].type[0],buffer2[t].type[1],buffer2[t].address, buffer2[t].length); }//显示目录函数结束

rd(name) //删除空目录 char *name; {

int dnum,bnum,t,i;

if((t=search(name,8,&dnum,&bnum))==false) {printf(\目录不存在\\n\ return(false); }

if(strcmp(name,”/”)==0) { printf(\该目录为根目录,不能删除\\n\ return(false); }

fseek(fc,t*64L, SEEK_SET); fread(buffer2,64,1,fc); flag=1;

for(i=0;i<8;i++)

if(buffer2[bnum].name[0]!='$')flag=0; if(flag==0) { printf(\该目录为非空目录,不能删除\\n\ return(false); }

fseek(fc,dnum*64L, SEEK_SET); fread(buffer2,64,1,fc); buffer2[bnum].name[0]='$'; fseek(fc,dnum*64L, SEEK_SET); fwrite(buffer2,64,1,fc);

fseek(fc,t/64*64, SEEK_SET); fread(buffer1,64,1,fc); t=buffer1[td]; buffer1[td]=0;

fseek(fc, t/64*64L, SEEK_SET); fwrite(buffer1,64,1,fc); }

typefile(name) //显示文件内容 char *name;

{int i,dnum,dn,bn,t;

if((dnum=search(name,1,&dn,&bn))==false) {printf(\文件不存在\\n\ return(false); }

if(sopen(name)!=-1)

{printf(\该文件打开,不能显示\\n\ return(false); }

while(dnum!='\\xff')

{fseek(fc,dnum*64L,SEEK_SET); fread(buffer1,64,1,fc); for(t=0;t<64&&buffer1[t]!='#';t++) //查找文件 //根目录不能删除 //判断该子目录是否为空目录 //非空目录不能删除 //删除空目录 //将该文件的目录置成空目录 //删除该子目录占用的盘块 //读一个盘块到缓冲 //显示缓冲内容

putchar(buffer1[t]); printf(\ }

//获得下一个盘块

fseek(fc, dnum/64*64L, SEEK_SET); fread(buffer1,64,1,fc); dnum=buffer1[dnumd]; }

}//显示文件函数结束

change(name,attribute)

//改变文件name的属性为attribute char *name; int attribute; {int dnum,bnum;

if(search(name,1,&dnum,&bnum)==false) //查找文件目录 { printf(\文件不存在\\n\ return(false); }

if(sopen(name)!=-1)

{ printf(\该文件打开,不能改变文件属性\\n\ return(false); }

fseek(fc,dnum*64L,SEEK_SET); fread(buffer2,64,1,fc); //读出该目录所在盘块 buffer2[bnum].attribute=attribute; //修改属性 fseek(fc,dnum*64L,SEEK_SET); fwrite(buffer2,64,1,fc); //写回磁盘 }//改变文件属性函数结束

main( )

{char name[20],buffer[64];

int attribute,type,length,i,a,j; //建立文件,模拟磁盘

if((fc=fopen(\ {printf(\无法打开文件\\n\ exit(0); }

//初始化已打开文件表 openfile.length=0; //初始化磁盘

//初始化文件分配表

buffer1[0]=buffer1[1]=buffer1[2]=255; //磁盘第0、1块存放FAT表,第2块存放根目录 for(i=3;i<64;i++) buffer1[i]=0;

buffer1[13]=buffer1[49]=254; //假定模拟磁盘中有两个坏盘块:第13块和49块 fwrite(buffer1,64L,1,fc); for(i=0;i<64;i++) buffer1[i]=0;

fwrite(buffer1,64L,1,fc); //初始化根目录

for(i=0;i<8;i++)

buffer2[i].name[0]='$'; //若目录项第一个字符为“$”表示该目录项为空 fwrite(buffer2,64L,1,fc); //初始化已打开文件表 while(1)

{printf(\结束\\n\ printf(\建立文件\\n\ printf(\打开文件\\n\ printf(\读文件\\n\

本文来源:https://www.bwwdw.com/article/luyf.html

Top