操作系统A

李美蓉

目录

  • 1 操作系统引论
    • 1.1 计算机操作方式的演变
    • 1.2 什么是操作系统
    • 1.3 操作系统的结构
    • 1.4 操作系统的特性
  • 2 进程管理
    • 2.1 进程描述
    • 2.2 进程状态及转换
    • 2.3 进程控制
    • 2.4 进程的并发执行
    • 2.5 进程同步与互斥
    • 2.6 信号量机制及应用
    • 2.7 生产者消费者问题
    • 2.8 读者写者问题
    • 2.9 哲学家就餐问题
    • 2.10 进程通信
    • 2.11 消息缓冲区队列机制
  • 3 线程
    • 3.1 什么是线程
    • 3.2 线程实现
  • 4 处理机调度
    • 4.1 处理机调度概述
    • 4.2 作业调度
    • 4.3 进程调度
    • 4.4 实时调度
  • 5 死锁
    • 5.1 死锁概念及资源分配图
    • 5.2 死锁的必要条件及预防
    • 5.3 死锁避免
    • 5.4 死锁检测和解除
  • 6 存储器管理
    • 6.1 存取器概述及连续分配方式(一)
    • 6.2 动态分区分配及可重定位分区分配
    • 6.3 分页存储管理
    • 6.4 页表结构
    • 6.5 分段及段页式存储管理方式
  • 7 虚拟存储器管理
    • 7.1 虚拟存储技术
    • 7.2 请求分页存储管理
    • 7.3 页面置换算法
    • 7.4 抖动与工作集
    • 7.5 请求分段存储管理
  • 8 磁盘存储器系统
    • 8.1 磁盘存储器的结构
    • 8.2 磁盘调度
    • 8.3 廉价磁盘冗余阵列(RAID)
    • 8.4 提高磁盘I/O的其他方法
    • 8.5 磁盘可靠性技术
  • 9 输入输出系统
    • 9.1 I/O硬件系统
    • 9.2 I/O控制方式
    • 9.3 中断处理程序及设备驱动程序
    • 9.4 设备无关性软件
    • 9.5 缓冲管理
    • 9.6 用户层软件及Spooling
  • 10 文件系统
    • 10.1 文件及逻辑结构
    • 10.2 文件目录结构
    • 10.3 连续分配及链接分配
    • 10.4 索引分配
    • 10.5 空闲空间管理
  • 11 操作系统实验
    • 11.1 实验一  Linux基本命令
    • 11.2 实验二 进程管理
    • 11.3 实验三 进程间通信
    • 11.4 实验四 处理机调度
    • 11.5 实验五 存储管理
    • 11.6 实验六 文件管理
实验六 文件管理

6.1  实验目的

1.加深对文件、目录和文件系统等概念的理解

2.通过设计一个简单的文件系统加深理解操作系统中文件文件系统的实现

6.2  背景知识

1.阅读Linux文件系统相关源码

6.3  实验内容

一、为Linux系统设计一个简单的二级文件系统。要求做到以下几点:

1.在内存中开辟一个虚拟磁盘空间作为文件存储分区,在其上实现一个简单的基于多级目录的单用户单任务系统中的文件系统。退出该文件系统的使用时,应将该虚拟文件系统以一个Windows 文件的方式保存到磁盘上,以便下次可以再将它恢复到内存的虚拟磁盘空间中。

2.文件存储空间的分配可采用显式链接分配或其他的办法。

3.空闲磁盘空间的管理可选择位示图或其他的办法。如果采用位示图来管理文件存储空间,并采用显式链接分配方式,那么可以将位示图合并到FAT中。

4.文件目录结构采用多级目录结构。为了简单起见,可以不使用索引结点,其中的每个目录项应包含文件名、物理地址、长度等信息,还可以通过目录项实现对文件的读和写的保护。

5.要求提供以下操作命令:

(1)my_format:对文件存储器进行格式化,即按照文件系统的结构对虚拟磁盘空间进行布局,并在其上创建根目录以及用于管理文件存储空间等的数据结构。

(2)my_mkdir:用于创建子目录。(根据设计思路,学生自行设计完成,画出程序流程图并编码实现)

(3)my_rmdir:用于删除子目录。

(4)my_ls:用于显示目录中的内容。

(5)my_cd:用于更改当前目录。

(6)my_create:用于创建文件。

(7)my_open:用于打开文件。根据设计思路,学生自行设计完成,画出程序流程图并编码实现)

(8)my_close:用于关闭文件。

(9)my_write:用于写文件。

(10)my_read:用于读文件。

(11)my_rm:用于删除文件。(根据设计思路,学生自行设计完成,画出程序流程图并编码实现)

(12)my_exitsys:用于退出文件系统。

二、设计思路

1.系统主函数main()

(1)对应命令:无

(2)命令调用格式:无

(3)函数设计格式:void main()

(4)功能:系统主函数

(5)输入:无

(6)输出:无

(7)函数需完成的工作:

① 对前面定义的全局变量进行初始化;

② 调用startsys()进入文件系统;

③ 列出文件系统提供的各项功能及命令调用格式;

④ 显示命令行提示符,等待用户输入命令;

⑤ 将用户输入的命令保存到一个buf中;

⑥ 对buf中的内容进行命令解析,并调用相应的函数执行用户键入的命令;

⑦ 如果命令不是“my_exitsys”,则命令执行完毕后转④。

2. 进入文件系统函数startsys()

(1)对应命令:无

(2)命令调用格式:无

(3)函数设计格式:void startsys()

(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)函数设计格式:void my_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_cd dirname

(3)函数设计格式:void my_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_ mkdir dirname

(3)函数设计格式:void my_mkdir(char *dirname)

(4)功能:在当前目录下创建名为dirname的子目录。

(5)输入:

dirname:新建目录的目录名。           

(6)输出:无。

(7)函数需完成的工作:

① 调用do_read()读入当前目录文件内容到内存,检查当前目录下新建目录文件是否重名,若重名则返回,并显示错误信息;

② 为新建子目录文件分配一个空闲打开文件表项,如果没有空闲表项则返回-1,并显示错误信息;

③ 检查FAT是否有空闲的盘块,如有则为新建目录文件分配一个盘块,否则释放①中分配的打开文件表项,返回,并显示错误信息;

④ 在当前目录中为新建目录文件寻找一个空闲的目录项或为其追加一个新的目录项;需修改当前目录文件的长度信息,并将当前目录文件的用户打开文件表项中的fcbstate置为1;

⑤ 准备好新建目录文件的FCB的内容,文件的属性为目录文件,以覆盖写方式调用do_write()将其填写到对应的空目录项中;

⑥ 在新建目录文件所分配到的磁盘块中建立两个特殊的目录项“.”和“..”目录项,方法是:首先在用户空间中准备好内容,然后以截断写或者覆盖写方式调用do_write()将其写到③中分配到的磁盘块中;

⑦ 返回。

6.删除子目录函数rmdir()

(1)对应命令:my_ rmdir

(2)命令调用格式:my_ rmdir dirname

(1)函数设计格式:void my_rmdir(char *dirname)

(2)功能:在当前目录下删除名为dirname的子目录。

(3)输入:

dirname:欲删除目录的目录名。          

(4)输出:无。

(5)函数需完成的工作:

① 调用do_read()读入当前目录文件内容到内存,检查当前目录下欲删除目录文件是否存在,若不存在则返回,并显示错误信息;

② 检查欲删除目录文件是否为空(除了“.”和“..”外没有其他子目录和文件),可根据其目录项中记录的文件长度来判断,若不为空则返回,并显示错误信息;

③ 检查该目录文件是否已经打开,若已打开则调用my_close()关闭掉;

④ 回收该目录文件所占据的磁盘块,修改FAT;

⑤ 从当前目录文件中清空该目录文件的目录项,且free字段置为0:以覆盖写方式调用do_write()来实现;

⑥ 修改当前目录文件的用户打开表项中的长度信息,并将表项中的fcbstate置为1;

⑦ 返回。

7.显示目录函数my_ls()

(1)对应命令:my_ls

(2)命令调用格式:my_ls

(3)函数设计格式:void my_ls(void)

(4)功能:显示当前目录的内容(子目录和文件信息)。

(5)输入:无           

(6)输出:无

(7)函数需完成的工作:

① 调用do_read()读出当前目录文件内容到内存;

② 将读出的目录文件的信息按照一定的格式显示到屏幕上;

③ 返回。 

8.创建文件函数my_create() 

(1)对应命令:my_create

(2)命令调用格式:my_create filename

(3)函数设计格式:int my_create (char *filename)

(4)功能:创建名为filename的新文件。

(5)输入:

filename:新建文件的文件名,可能包含路径。          

(6)输出:若创建成功,返回该文件的文件描述符(文件打开表中的数组下标);否则返回-1。

(7)函数需完成的工作:

① 为新文件分配一个空闲打开文件表项,如果没有空闲表项则返回-1,并显示错误信息;

② 若新文件的父目录文件还没有打开,则调用my_open()打开;若打开失败,则释放①中为新建文件分配的空闲文件打开表项,返回-1,并显示错误信息;

③ 调用do_read()读出该父目录文件内容到内存,检查该目录下新文件是否重名,若重名则释放①中分配的打开文件表项,并调用my_close()关闭②中打开的目录文件;然后返回-1,并显示错误信息;

④ 检查FAT是否有空闲的盘块,如有则为新文件分配一个盘块,否则释放①中分配的打开文件表项,并调用my_close()关闭②中打开的目录文件;返回-1,并显示错误信息;

⑤ 在父目录中为新文件寻找一个空闲的目录项或为其追加一个新的目录项;需修改该目录文件的长度信息,并将该目录文件的用户打开文件表项中的fcbstate置为1;

⑥ 准备好新文件的FCB的内容,文件的属性为数据文件,长度为0,以覆盖写方式调用do_write()将其填写到⑤中分配到的空目录项中;

⑦ 为新文件填写①中分配到的空闲打开文件表项,fcbstate字段值为0,读写指针值为0;

⑧ 调用my_close()关闭②中打开的父目录文件;

⑨ 将新文件的打开文件表项序号作为其文件描述符返回。

9.删除文件函数my_rm()

(1)对应命令:my_rm

(2)命令调用格式:my_rm filename

(3)函数设计格式:void my_rm(char *filename)

(4)功能:删除名为filename的文件。

(5)输入:

filename:欲删除文件的文件名,可能还包含路径。          

(6)输出:无。

(7)函数需完成的工作:

① 若欲删除文件的父目录文件还没有打开,则调用my_open()打开;若打开失败,则返回,并显示错误信息;

② 调用do_read()读出该父目录文件内容到内存,检查该目录下欲删除文件是否存在,若不存在则返回,并显示错误信息;

③ 检查该文件是否已经打开,若已打开则关闭掉;

④ 回收该文件所占据的磁盘块,修改FAT;

⑤ 从文件的父目录文件中清空该文件的目录项,且free字段置为0:以覆盖写方式调用do_write()来实现;;

⑥ 修改该父目录文件的用户打开文件表项中的长度信息,并将该表项中的fcbstate置为1;

⑦ 返回。

10.打开文件函数my_open()

(1)对应命令:my_open

(2)命令调用格式:my_open filename

(3)函数设计格式:int my_open(char *filename)

(4)功能:打开当前目录下名为filename的文件。

(5)输入:

filename:欲打开文件的文件名          

(6)输出:若打开成功,返回该文件的描述符(在用户打开文件表中表项序号);否则返回-1。

(7)函数需完成的工作:

① 检查该文件是否已经打开,若已打开则返回-1,并显示错误信息;

② 调用do_read()读出父目录文件的内容到内存,检查该目录下欲打开文件是否存在,若不存在则返回-1,并显示错误信息;

③ 检查用户打开文件表中是否有空表项,若有则为欲打开文件分配一个空表项,若没有则返回-1,并显示错误信息;

④ 为该文件填写空白用户打开文件表表项内容,读写指针置为0;

⑤ 将该文件所分配到的空白用户打开文件表表项序号(数组下标)作为文件描述符fd返回。

11.关闭文件函数my_close()

(1)对应命令:my_close

(2)命令调用格式:my_close fd

(3)函数设计格式:void my_close(int fd)

(4)功能:关闭前面由my_open()打开的文件描述符为fd的文件。

(5)输入:

fd:文件描述符。           

(6)输出:无。

(7)函数需完成的工作:

① 检查fd的有效性(fd不能超出用户打开文件表所在数组的最大下标),如果无效则返回-1;

② 检查用户打开文件表表项中的fcbstate字段的值,如果为1则需要将该文件的FCB的内容保存到虚拟磁盘上该文件的目录项中,方法是:打开该文件的父目录文件,以覆盖写方式调用do_write()将欲关闭文件的FCB写入父目录文件的相应盘块中;

③ 回收该文件占据的用户打开文件表表项(进行清空操作),并将topenfile字段置为0;

④ 返回。

12.写文件函数my_write() 

(1)对应命令:my_write

(2)命令调用格式:my_write fd

(3)函数设计格式:int my_write(int fd)

(4)功能:将用户通过键盘输入的内容写到fd所指定的文件中。磁盘文件的读写操作都必须以完整的数据块为单位进行,在写操作时,先将数据写在缓冲区中,缓冲区的大小与磁盘块的大小相同,然后再将缓冲区中的数据一次性写到磁盘块中;读出时先将一个磁盘块中的内容读到缓冲区中,然后再传送到用户区。本实例为了简便起见,没有设置缓冲区管理,只是在读写文件时由用户使用malloc()申请一块空间作为缓冲区,读写操作结束后使用free()释放掉。

写操作常有三种方式:截断写、覆盖写和追加写。截断写是放弃原来文件的内容,重新写文件;覆盖写是修改文件在当前读写指针所指的位置开始的部分内容;追加写是在原文件的最后添加新的内容。在本实例中,输入写文件命令后,系统会出现提示让用户选择其中的一种写方式,并将随后键盘输入的内容按照所选的方式写到文件中,键盘输入内容通过CTR+Z键(或其他设定的键)结束。

(5)输入:

fd: open()函数的返回值,文件的描述符;      

(6)输出:实际写入的字节数。

(7)函数需完成的工作:

① 检查fd的有效性(fd不能超出用户打开文件表所在数组的最大下标),如果无效则返回-1,并显示出错信息;

② 提示并等待用户输入写方式:(1:截断写;2:覆盖写;3:追加写)

③ 如果用户要求的写方式是截断写,则释放文件除第一块外的其他磁盘空间内容(查找并修改FAT表),将内存用户打开文件表项中文件长度修改为0,将读写指针置为0并转④;如果用户要求的写方式是追加写,则修改文件的当前读写指针位置到文件的末尾,并转④;如果写方式是覆盖写,则直接转④;

④ 提示用户:整个输入内容通过CTR+Z键(或其他设定的键)结束;用户可分多次输入写入内容,每次用回车结束;

⑤ 等待用户从键盘输入文件内容,并将用户的本次输入内容保存到一临时变量text[]中,要求每次输入以回车结束,全部结束用CTR+Z键(或其他设定的键);

⑥ 调用do_write()函数将通过键盘键入的内容写到文件中。

⑦ 如果do_write()函数的返回值为非负值,则将实际写入字节数增加do_write()函数返回值,否则显示出错信息,并转⑨;

⑧ 如果text[]中最后一个字符不是结束字符CTR+Z,则转⑦继续进行写操作;否则转⑨;

⑨ 如果当前读写指针位置大于用户打开文件表项中的文件长度,则修改打开文件表项中的文件长度信息,并将fcbstate置1;

⑩ 返回实际写入的字节数。

13.实际写文件函数do_write() 

(1)对应命令:无

(2)命令调用格式:无

(3)函数设计格式:int my_write(int fd,char *text,int len,char wstyle)

(4)功能:被写文件函数my_write()调用,用来将键盘输入的内容写到相应的文件中去。

(5)输入:

fd: open()函数的返回值,文件的描述符;

text:指向要写入的内容的指针;

len:本次要求写入字节数     

wstyle:写方式

(6)输出:实际写入的字节数。

(7)函数需完成的工作:

① 用malloc()申请1024B的内存空间作为读写磁盘的缓冲区buf,申请失败则返回-1,并显示出错信息;

② 将读写指针转化为逻辑块块号和块内偏移off,并利用打开文件表表项中的首块号及FAT表的相关内容将逻辑块块号转换成对应的磁盘块块号blkno;如果找不到对应的磁盘块,则需要检索FAT为该逻辑块分配一新的磁盘块,并将对应的磁盘块块号blkno登记到FAT中,若分配失败,则返回-1,并显示出错信息;

③ 如果是覆盖写,或者如果当前读写指针所对应的块内偏移off不等于0,则将块号为blkno的虚拟磁盘块全部1024B的内容读到缓冲区buf中;否则便用ASCII码0清空buf;

④ 将text中未写入的内容暂存到缓冲区buff的第off字节开始的位置,直到缓冲区满,或者接收到结束字符CTR+Z为止;将本次写入字节数记录到tmplen中;

⑤ 将buf中1024B的内容写入到块号为blkno的虚拟磁盘块中;

⑥将当前读写指针修改为原来的值加上tmplen;并将本次实际写入的字节数增加tmplen;

⑦ 如果tmplen小于len,则转②继续写入;否则转⑧;

⑧ 返回本次实际写入的字节数。

14.读文件函数my_read() 

(1)对应命令:my_read

(2)命令调用格式:my_read fd len

(3)函数设计格式:int myread (int fd, int len)

(4)功能:读出指定文件中从读写指针开始的长度为len的内容到用户空间中。

(5)输入:

fd: open()函数的返回值,文件的描述符;

len:   要从文件中读出的字节数。        

(6)输出:实际读出的字节数。

(7)函数需完成的工作:

① 定义一个字符型数组text[len],用来接收用户从文件中读出的文件内容;

② 检查fd的有效性(fd不能超出用户打开文件表所在数组的最大下标),如果无效则返回-1,并显示出错信息;

③ 调用do_read()将指定文件中的len字节内容读出到text[]中;

④ 如果do_read()的返回值为负,则显示出错信息;否则将text[]中的内容显示到屏幕上;

⑤ 返回。

15.实际读文件函数do_read() 

(1)对应命令:无

(2)命令调用格式:无

(3)函数设计格式:int do_read (int fd, int len,char *text)

(4)功能:被my_read()调用,读出指定文件中从读写指针开始的长度为len的内容到用户空间的text中。

(5)输入:

fd: open()函数的返回值,文件的描述符;

len:   要求从文件中读出的字节数。        

text:指向存放读出数据的用户区地址

(6)输出:实际读出的字节数。

(7)函数需完成的工作:

① 使用malloc()申请1024B空间作为缓冲区buf,申请失败则返回-1,并显示出错信息;

② 将读写指针转化为逻辑块块号及块内偏移量off,利用打开文件表表项中的首块号查找FAT表,找到该逻辑块所在的磁盘块块号;将该磁盘块块号转化为虚拟磁盘上的内存位置;

③ 将该内存位置开始的1024B(一个磁盘块)内容读入buf中;

④ 比较buf中从偏移量off开始的剩余字节数是否大于等于应读写的字节数len,如果是,则将从off开始的buf中的len长度的内容读入到text[]中;否则,将从off开始的buf中的剩余内容读入到text[]中;

⑤ 将读写指针增加④中已读字节数,将应读写的字节数len减去④中已读字节数,若len大于0,则转②;否则转⑥;

⑥ 使用free()释放①中申请的buf。

⑦ 返回实际读出的字节数。

16. 退出文件系统函数my_exitsys() 

(1)对应命令:my_exitsys

(2)命令调用格式:my_ exitsys

(1)函数设计格式:void my_exitsys()

(2)功能:退出文件系统。

(3)输入:无    

(4)输出:无。

(5)函数需完成的工作:

① 使用C库函数fopen()打开磁盘上的myfsys文件;

② 将虚拟磁盘空间中的所有内容保存到磁盘上的myfsys文件中;

③ 使用c语言的库函数fclose()关闭myfsys文件;

④ 撤销用户打开文件表,释放其内存空间

⑤ 释放虚拟磁盘空间。

三、主要数据结构

1.文件控制块

typedef struct FCB{

 

    char filename[8];  // 文件名

    char exname[3];  // 文件扩展名

    unsigned charattribute;  // 文件属性字段,值为0时表示目录文件,值为1时表示数据文件

    unsigned short time;  // 文件创建时间

    unsigned short date;  // 文件创建日期

    unsigned short first;  // 文件起始盘块号

    unsigned long length;  // 文件长度

    char free;  // 表示目录项是否为空,若值为0,表示空,值为1,表示已分配

}fcb;

2. 文件分配表

typedef struct FAT

{

    unsigned short id;  // 磁盘块的状态(空闲的,最后的,下一个)

}fat;

3. 用户打开文件表

typedef struct USEROPEN

{

    char filename[8];  // 文件名

    char exname[3];  // 文件扩展名

    unsigned char attribute;//文件属性字段,值为0时表示目录文件,值为1时表示数据文件

    unsigned short time;  // 文件创建时间

    unsigned short date;  // 文件创建日期

    unsigned short first;  // 文件起始盘块号

    unsigned long length;//文件长度(对数据文件是字节数,对目录文件可以是目录项个数)

    char free;  // 表示目录项是否为空,若值为0,表示空,值为1,表示已分配

 

    unsigned short dirno;  // 相应打开文件的目录项在父目录文件中的盘块号

    int diroff;  // 相应打开文件的目录项在父目录文件的dirno盘块中的目录项序号

    char dir[80];  // 相应打开文件所在的路径名,这样方便快速检查出指定文件是否已经打开

    int father;  // 父目录在打开文件表项的位置

    int count;  // 读写指针在文件中的位置,文件的总字符数

    char fcbstate;  // 是否修改了文件的FCB的内容,如果修改了置为1,否则为0

    char topenfile;  // 表示该用户打开表项是否为空,若值为0,表示为空,否则表示已被某打开文件占据

}useropen;

4. 引导块

typedef struct BLOCK0

{

    char magic[10];  // 文件系统魔数

    char information[200];//存储一些描述信息,如磁盘块大小、磁盘块数量、最多打开文件数等

    unsigned short root;  // 根目录文件的起始盘块号

    unsigned char*startblock;  // 虚拟磁盘上数据区开始位置

}block0;

四、主要函数

void startsys();  // 进入文件系统

void my_format();  // 磁盘格式化

void my_cd(char *dirname);  //更改当前目录

void my_mkdir(char *dirname); // 创建子目录

void my_rmdir(char *dirname); // 删除子目录

void my_ls();  // 显示目录

void my_create (char *filename); // 创建文件

void my_rm(char *filename); // 删除文件

int my_open(char *filename); // 打开文件

int my_close(int fd);  // 关闭文件

int my_write(int fd);  // 写文件

int do_write(int fd, char *text, int len, char wstyle);  // 实际写文件

int my_read (int fd, int len); // 读文件

int do_read (int fd, int len,char *text);  // 实际读文件

void my_exitsys();  // 退出文件系统

unsigned short findblock(); // 寻找空闲盘块

int findopenfile();  // 寻找空闲文件表项

五、参考程序

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>


#define BLOCKSIZE 1024  // 磁盘块大小

#define SIZE 1024000  // 虚拟磁盘空间大小

#define END 65535  // FAT中的文件结束标志

#define FREE 0  // FAT中盘块空闲标志

#define ROOTBLOCKNUM 2  // 根目录区所占盘块数

#define MAXOPENFILE 10  // 最多同时打开文件个数t

#define MAXTEXT 10000


/* 文件控制块 */

typedef struct FCB{


    char filename[8];  // 文件名

    char exname[3];  // 文件扩展名

    unsigned char attribute;  // 文件属性字段,值为0时表示目录文件,值为1时表示数据文件

    unsigned short time;  // 文件创建时间

    unsigned short date;  // 文件创建日期

    unsigned short first;  // 文件起始盘块号

    unsigned long length;  // 文件长度

    char free;  // 表示目录项是否为空,若值为0,表示空,值为1,表示已分配

}fcb;


/* 文件分配表 */

typedef struct FAT

{

    unsigned short id;  // 磁盘块的状态(空闲的,最后的,下一个)

}fat;

/* 用户打开文件表 */

typedef struct USEROPEN

{

    char filename[8];  // 文件名

    char exname[3];  // 文件扩展名

    unsigned char attribute;//文件属性字段,值为0时表示目录文件,值为1时表示数据文件

    unsigned short time;  // 文件创建时间

    unsigned short date;  // 文件创建日期

    unsigned short first;  // 文件起始盘块号

    unsigned long length;//文件长度(对数据文件是字节数,对目录文件可以是目录项个数)

    char free;  // 表示目录项是否为空,若值为0,表示空,值为1,表示已分配


    unsigned short dirno;  // 相应打开文件的目录项在父目录文件中的盘块号

    int diroff;  // 相应打开文件的目录项在父目录文件的dirno盘块中的目录项序号

    char dir[80];  // 相应打开文件所在的路径名,这样方便快速检查出指定文件是否已经打开

    int father;  // 父目录在打开文件表项的位置

    int count;  // 读写指针在文件中的位置,文件的总字符数

    char fcbstate;  // 是否修改了文件的FCB的内容,如果修改了置为1,否则为0

    char topenfile;  // 表示该用户打开表项是否为空,若值为0,表示为空,否则表示已被某打开文件占据

}useropen;


/* 引导块 */

typedef struct BLOCK0

{

    char magic[10];  // 文件系统魔数

    char information[200];//存储一些描述信息,如磁盘块大小、磁盘块数量、最多打开文件数等

    unsigned short root;  // 根目录文件的起始盘块号

    unsigned char *startblock;  // 虚拟磁盘上数据区开始位置

}block0;

unsigned char *myvhard;  // 指向虚拟磁盘的起始地址

useropen openfilelist[MAXOPENFILE];  // 用户打开文件表数组

int curdir;  // 用户打开文件表中的当前目录所在打开文件表项的位置

char currentdir[80];  // 记录当前目录的目录名(包括目录的路径)

unsigned char* startp;  // 记录虚拟磁盘上数据区开始位置

char myfilename[] = "myfilesys";//文件系统的文件名


void startsys();  // 进入文件系统

void my_format();  // 磁盘格式化

void my_cd(char *dirname);  // 更改当前目录

void my_mkdir(char *dirname);  // 创建子目录

void my_rmdir(char *dirname);  // 删除子目录

void my_ls();  // 显示目录

void my_create (char *filename);  // 创建文件

void my_rm(char *filename);  // 删除文件

int my_open(char *filename);  // 打开文件

int my_close(int fd);  // 关闭文件

int my_write(int fd);  // 写文件

int do_write(int fd, char *text, int len, char wstyle);  // 实际写文件

int my_read (int fd, int len);  // 读文件

int do_read (int fd, int len,char *text);  // 实际读文件

void my_exitsys();  // 退出文件系统

unsigned short findblock();  // 寻找空闲盘块

int findopenfile();  // 寻找空闲文件表项

void startsys()

{

    FILE *fp;//文件类型的指针变量

    unsigned char buf[SIZE];

    fcb *root;

    int i;

    myvhard = (unsigned char *)malloc(SIZE);//申请虚拟磁盘空间,用内存模拟磁盘空间

    memset(myvhard, 0, SIZE);//将myvhard中前SIZE个字节用 0 替换并返回 myvhard

    if((fp = fopen(myfilename, "r")) != NULL)

    {

        fread(buf, SIZE, 1, fp);//将二进制文件读取到缓冲区

        fclose(fp);

        if(strcmp(((block0 *)buf)->magic, "10101010"))

        {

            printf("myfilesys is not exist,begin to creat the file...\n");

            my_format();

        }

        else

        {

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

                myvhard[i] = buf[i];

        }

    }

    else

    {

        printf("myfilesys is not exist,begin to creat the file...\n");

        my_format();

    }

    root = (fcb *)(myvhard + 5 * BLOCKSIZE);

    strcpy(openfilelist[0].filename, root->filename);

    strcpy(openfilelist[0].exname, root->exname);

    openfilelist[0].attribute = root->attribute;

    openfilelist[0].time = root->time;

    openfilelist[0].date = root->date;

    openfilelist[0].first = root->first;

    openfilelist[0].length = root->length;

    openfilelist[0].free = root->free;

    openfilelist[0].dirno = 5;

    openfilelist[0].diroff = 0;

    strcpy(openfilelist[0].dir, "\\root\\");

    openfilelist[0].father = 0;

    openfilelist[0].count = 0;

    openfilelist[0].fcbstate = 0;

    openfilelist[0].topenfile = 1;

    for(i = 1; i < MAXOPENFILE; i++)

        openfilelist[i].topenfile = 0;

    curdir = 0;

    strcpy(currentdir, "\\root\\");

    startp = ((block0 *)myvhard)->startblock;

}

void my_format()

{

    FILE *fp;

    fat *fat1, *fat2;

    block0 *blk0;

    time_t now;

    struct tm *nowtime;

    fcb *root;

    int i;

    blk0 = (block0 *)myvhard;

    fat1 = (fat *)(myvhard + BLOCKSIZE);

    fat2 = (fat *)(myvhard + 3 * BLOCKSIZE);

    root = (fcb *)(myvhard + 5 * BLOCKSIZE);

    strcpy(blk0->magic, "10101010");

    strcpy(blk0->information, "My FileSystem Ver 1.0 \n Blocksize=1KB Whole size=1000KB Blocknum=1000 RootBlocknum=2\n");

    blk0->root = 5;

    blk0->startblock = (unsigned char *)root;

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

    {

        fat1->id = END;

        fat2->id = END;

        fat1++;

        fat2++;

    }

    fat1->id = 6;

    fat2->id = 6;

    fat1++;

    fat2++;

    fat1->id = END;

    fat2->id = END;

    fat1++;

    fat2++;

    for(i = 7; i < SIZE / BLOCKSIZE; i++)

    {

        fat1->id = FREE;

        fat2->id = FREE;

        fat1++;

        fat2++;

    }

    now = time(NULL);

    nowtime = localtime(&now);

    strcpy(root->filename, ".");

    strcpy(root->exname, "");

    root->attribute = 0x28;

    root->time = nowtime->tm_hour * 2048 + nowtime->tm_min * 32 + nowtime->tm_sec / 2;

    root->date = (nowtime->tm_year - 80) * 512 + (nowtime->tm_mon + 1) * 32 + nowtime->tm_mday;

    root->first = 5;

    root->length = 2 * sizeof(fcb);

    root->free = 1;

    root++;

    now = time(NULL);

    nowtime = localtime(&now);

    strcpy(root->filename, "..");

    strcpy(root->exname, "");

    root->attribute = 0x28;

    root->time = nowtime->tm_hour * 2048 + nowtime->tm_min * 32 + nowtime->tm_sec / 2;

    root->date = (nowtime->tm_year - 80) * 512 + (nowtime->tm_mon + 1) * 32 + nowtime->tm_mday;

    root->first = 5;

    root->length = 2 * sizeof(fcb);

    root->free = 1;

    fp = fopen(myfilename, "w");

    fwrite(myvhard, SIZE, 1, fp);

    fclose(fp);

}

void my_cd(char *dirname)

{

    char *dir;

    int fd;

    dir = strtok(dirname, "\\");//分解字符串为一组字符串。dirname为要分解的字符串,"\\"为分隔符字符串

    if(strcmp(dir, ".") == 0)

        return;

    else if(strcmp(dir, "..") == 0)

    {

        if(curdir)

            curdir = my_close(curdir);

        return;

    }

    else if(strcmp(dir, "root") == 0)

    {

        while(curdir)

            curdir = my_close(curdir);

        dir = strtok(NULL, "\\");

    }

    while(dir)

    {

        fd = my_open(dir);

        if(fd != -1)

            curdir = fd;

        else

            return;

        dir = strtok(NULL, "\\");

    }

}

void my_mkdir(char *dirname)

{

    fcb *fcbptr;

    fat *fat1, *fat2;

    time_t now;

    struct tm *nowtime;

    char text[MAXTEXT];

    unsigned short blkno;

    int rbn, fd, i;

    fat1 = (fat *)(myvhard + BLOCKSIZE);

    fat2 = (fat *)(myvhard + 3 * BLOCKSIZE);

    openfilelist[curdir].count = 0;

    rbn = do_read(curdir, openfilelist[curdir].length, text);

    fcbptr = (fcb *)text;

    for(i = 0; i < rbn / sizeof(fcb); i++)//在当前目录下找,是否有重名目录

    {

        if(strcmp(fcbptr->filename, dirname) == 0 && strcmp(fcbptr->exname, "") == 0)

        {

            printf("Error,the dirname is already exist!\n");

            return;

        }

        fcbptr++;

    }

    fcbptr = (fcb *)text;

    for(i = 0; i < rbn / sizeof(fcb); i++)

    {

        if(fcbptr->free == 0)

            break;

        fcbptr++;

    }

    blkno = findblock();//寻找空闲盘块

    if(blkno == -1)

        return;

    (fat1 + blkno)->id = END;

    (fat2 + blkno)->id = END;

    now = time(NULL);

    nowtime = localtime(&now);

    strcpy(fcbptr->filename, dirname);

    strcpy(fcbptr->exname, "");

    fcbptr->attribute = 0x30;

    fcbptr->time = nowtime->tm_hour * 2048 + nowtime->tm_min * 32 + nowtime->tm_sec / 2;

    fcbptr->date = (nowtime->tm_year - 80) * 512 + (nowtime->tm_mon + 1) * 32 + nowtime->tm_mday;

    fcbptr->first = blkno;

    fcbptr->length = 2 * sizeof(fcb);

    fcbptr->free = 1;

    openfilelist[curdir].count = i * sizeof(fcb);//把当前目录的文件读写指针定位到文件末尾

    do_write(curdir, (char *)fcbptr, sizeof(fcb), 2);//从指针fcbptr开始写一个fcb大小的内容到当前目录文件末尾


    fd = my_open(dirname);//返回新建立的目录文件在用户打开文件数组的下标

    if(fd == -1)

        return;

    fcbptr = (fcb *)malloc(sizeof(fcb));//建立新目录的'.','..'目录

    now = time(NULL);

    nowtime = localtime(&now);

    strcpy(fcbptr->filename, ".");

    strcpy(fcbptr->exname, "");

    fcbptr->attribute = 0x28;

    fcbptr->time = nowtime->tm_hour * 2048 + nowtime->tm_min * 32 + nowtime->tm_sec / 2;

    fcbptr->date = (nowtime->tm_year - 80) * 512 + (nowtime->tm_mon + 1) * 32 + nowtime->tm_mday;

    fcbptr->first = blkno;

    fcbptr->length = 2 * sizeof(fcb);

    fcbptr->free = 1;

    do_write(fd, (char *)fcbptr, sizeof(fcb), 2);

    now = time(NULL);

    nowtime = localtime(&now);

    strcpy(fcbptr->filename, "..");

    strcpy(fcbptr->exname, "");

    fcbptr->attribute = 0x28;

    fcbptr->time = nowtime->tm_hour * 2048 + nowtime->tm_min * 32 + nowtime->tm_sec / 2;

    fcbptr->date = (nowtime->tm_year - 80) * 512 + (nowtime->tm_mon + 1) * 32 + nowtime->tm_mday;

    fcbptr->first = blkno;

    fcbptr->length = 2 * sizeof(fcb);

    fcbptr->free = 1;

    do_write(fd, (char *)fcbptr, sizeof(fcb), 2);

    free(fcbptr);

    my_close(fd);


    fcbptr = (fcb *)text;

    fcbptr->length = openfilelist[curdir].length;

    openfilelist[curdir].count = 0;

    do_write(curdir, (char *)fcbptr, sizeof(fcb), 2);//更新当前目录文件的内容

    openfilelist[curdir].fcbstate = 1;

}


void my_rmdir(char *dirname)

{

    fcb *fcbptr,*fcbptr2;

    fat *fat1, *fat2, *fatptr1, *fatptr2;

    char text[MAXTEXT], text2[MAXTEXT];

    unsigned short blkno;

    int rbn, rbn2, fd, i, j;

    fat1 = (fat *)(myvhard + BLOCKSIZE);

    fat2 = (fat *)(myvhard + 3 * BLOCKSIZE);

    if(strcmp(dirname, ".") == 0 || strcmp(dirname, "..") == 0)

    {

        printf("Error,can't remove this directory.\n");

        return;

    }

    openfilelist[curdir].count = 0;

    rbn = do_read(curdir, openfilelist[curdir].length, text);

    fcbptr = (fcb *)text;

    for(i = 0; i < rbn / sizeof(fcb); i++)//查找要删除的目录

    {

        if(strcmp(fcbptr->filename, dirname) == 0 && strcmp(fcbptr->exname, "") == 0)

            break;

        fcbptr++;

    }

    if(i == rbn / sizeof(fcb))

    {

        printf("Error,the directory is not exist.\n");

        return;

    }

    fd = my_open(dirname);//目录在当前打开文件数组中的下标

    rbn2 = do_read(fd, openfilelist[fd].length, text2);//读取要删除的目录的内容

    fcbptr2 = (fcb *)text2;

    for(j = 0; j < rbn2 / sizeof(fcb); j++)//判断要删除目录是否为空

    {

        if(strcmp(fcbptr2->filename, ".") && strcmp(fcbptr2->filename, "..") && strcmp(fcbptr2->filename, ""))

        {

            my_close(fd);

            printf("Error,the directory is not empty.\n");

            return;

        }

        fcbptr2++;

    }

    blkno = openfilelist[fd].first;

    while(blkno != END)//修改要删除目录在fat中所占用的目录项的属性

    {

        fatptr1 = fat1 + blkno;

        fatptr2 = fat2 + blkno;

        blkno = fatptr1->id;

        fatptr1->id = FREE;

        fatptr2->id = FREE;

    }

    my_close(fd);

    strcpy(fcbptr->filename, "");//修改已删除目录在当前目录的fcb的属性

    fcbptr->free = 0;

    openfilelist[curdir].count = i * sizeof(fcb);//更新当前目录文件的内容

    do_write(curdir, (char *)fcbptr, sizeof(fcb), 2);

    openfilelist[curdir].fcbstate = 1;

}

void my_ls()

{

    fcb *fcbptr;

    char text[MAXTEXT];

    int rbn, i;

    openfilelist[curdir].count = 0;

    rbn = do_read(curdir, openfilelist[curdir].length, text);

    fcbptr = (fcb *)text;

    for(i = 0; i < rbn / sizeof(fcb); i++)

    {

        if(fcbptr->free)

        {

            if(fcbptr->attribute & 0x20)

                printf("%s\\\t\t<DIR>\t\t%d/%d/%d\t%02d:%02d:%02d\n", fcbptr->filename, (fcbptr->date >> 9) + 1980, (fcbptr->date >> 5) & 0x000f, fcbptr->date & 0x001f, fcbptr->time >> 11, (fcbptr->time >> 5) & 0x003f, fcbptr->time & 0x001f * 2);

            else

                printf("%s.%s\t\t%dB\t\t%d/%d/%d\t%02d:%02d:%02d\t\n", fcbptr->filename, fcbptr->exname, (int)(fcbptr->length), (fcbptr->date >> 9) + 1980, (fcbptr->date >> 5) & 0x000f, fcbptr->date & 0x1f, fcbptr->time >> 11, (fcbptr->time >> 5) & 0x3f, fcbptr->time & 0x1f * 2);

        }

        fcbptr++;

    }

}

void my_create(char *filename)

{

    fcb *fcbptr;

    fat *fat1, *fat2;

    char *fname, *exname, text[MAXTEXT];

    unsigned short blkno;

    int rbn, i;

    time_t now;

    struct tm *nowtime;

    fat1 = (fat *)(myvhard + BLOCKSIZE);

    fat2 = (fat *)(myvhard + BLOCKSIZE);

    fname = strtok(filename, ".");

    exname = strtok(NULL, ".");

    if(strcmp(fname, "") == 0)

    {

        printf("Error,creating file must have a right name.\n");

        return;

    }

    if(!exname)

    {

        printf("Error,creating file must have a extern name.\n");

        return;

    }

    openfilelist[curdir].count = 0;

    rbn = do_read(curdir, openfilelist[curdir].length, text);

    fcbptr = (fcb *)text;

    for(i = 0; i < rbn / sizeof(fcb); i++)

    {

        if(strcmp(fcbptr->filename, fname) == 0 && strcmp(fcbptr->exname, exname) == 0)

        {

            printf("Error,the filename is already exist!\n");

            return;

        }

        fcbptr++;

    }

    fcbptr = (fcb *)text;

    for(i = 0; i < rbn / sizeof(fcb); i++)

    {

        if(fcbptr->free == 0)

            break;

        fcbptr++;

    }

    blkno = findblock();

    if(blkno == -1)

        return;

    (fat1 + blkno)->id = END;

    (fat2 + blkno)->id = END;


    now = time(NULL);

    nowtime = localtime(&now);

    strcpy(fcbptr->filename, fname);

    strcpy(fcbptr->exname, exname);

    fcbptr->attribute = 0x00;

    fcbptr->time = nowtime->tm_hour * 2048 + nowtime->tm_min * 32 + nowtime->tm_sec / 2;

    fcbptr->date = (nowtime->tm_year - 80) * 512 + (nowtime->tm_mon + 1) * 32 + nowtime->tm_mday;

    fcbptr->first = blkno;

    fcbptr->length = 0;

    fcbptr->free = 1;

    openfilelist[curdir].count = i * sizeof(fcb);

    do_write(curdir, (char *)fcbptr, sizeof(fcb), 2);

    fcbptr = (fcb *)text;

    fcbptr->length = openfilelist[curdir].length;

    openfilelist[curdir].count = 0;

    do_write(curdir, (char *)fcbptr, sizeof(fcb), 2);

    openfilelist[curdir].fcbstate = 1;

}

void my_rm(char *filename)

{

    fcb *fcbptr;

    fat *fat1, *fat2, *fatptr1, *fatptr2;

    char *fname, *exname, text[MAXTEXT];

    unsigned short blkno;

    int rbn, i;

    fat1 = (fat *)(myvhard + BLOCKSIZE);

    fat2 = (fat *)(myvhard + 3 * BLOCKSIZE);

    fname = strtok(filename, ".");

    exname = strtok(NULL, ".");

    if(strcmp(fname, "") == 0)

    {

        printf("Error,removing file must have a right name.\n");

        return;

    }

    if(!exname)

    {

        printf("Error,removing file must have a extern name.\n");

        return;

    }

    openfilelist[curdir].count = 0;

    rbn = do_read(curdir, openfilelist[curdir].length, text);

    fcbptr = (fcb *)text;

    for(i = 0; i < rbn / sizeof(fcb); i++)

    {

        if(strcmp(fcbptr->filename, fname) == 0 && strcmp(fcbptr->exname, exname) == 0)

            break;

        fcbptr++;

    }

    if(i == rbn / sizeof(fcb))

    {

        printf("Error,the file is not exist.\n");

        return;

    }

    blkno = fcbptr->first;

    while(blkno != END)

    {

        fatptr1 = fat1 + blkno;

        fatptr2 = fat2 + blkno;

        blkno = fatptr1->id;

        fatptr1->id = FREE;

        fatptr2->id = FREE;

    }

    strcpy(fcbptr->filename, "");

    fcbptr->free = 0;

    openfilelist[curdir].count = i * sizeof(fcb);

    do_write(curdir, (char *)fcbptr, sizeof(fcb), 2);

    openfilelist[curdir].fcbstate = 1;

}

int my_open(char *filename)

{

    fcb *fcbptr;

    char *fname, exname[3], *str, text[MAXTEXT];

    int rbn, fd, i;

    fname = strtok(filename, ".");

    str = strtok(NULL, ".");

    if(str)

        strcpy(exname, str);

    else

        strcpy(exname, "");

    for(i = 0; i < MAXOPENFILE; i++)//在用户打开文件数组查找看当前文件是否已经打开

    {

        if(strcmp(openfilelist[i].filename, fname) == 0 && strcmp(openfilelist[i].exname, exname) == 0 && i != curdir)

        {

            printf("Error,the file is already open.\n");

            return -1;

        }

    }

    openfilelist[curdir].count = 0;

    rbn = do_read(curdir, openfilelist[curdir].length, text);

    fcbptr = (fcb *)text;

    for(i = 0; i < rbn / sizeof(fcb); i++)//在当前目录下找要打开的文件是否存在

    {

        if(strcmp(fcbptr->filename, fname) == 0 && strcmp(fcbptr->exname, exname) == 0)

            break;

        fcbptr++;

    }

    if(i == rbn / sizeof(fcb))

    {

        printf("Error,the file is not exist.\n");

        return -1;

    }

    fd = findopenfile();//寻找空闲文件表项

    if(fd == -1)

        return -1;

    strcpy(openfilelist[fd].filename, fcbptr->filename);

    strcpy(openfilelist[fd].exname, fcbptr->exname);

    openfilelist[fd].attribute = fcbptr->attribute;

    openfilelist[fd].time = fcbptr->time;

    openfilelist[fd].date = fcbptr->date;

    openfilelist[fd].first = fcbptr->first;

    openfilelist[fd].length = fcbptr->length;

    openfilelist[fd].free = fcbptr->free;

    openfilelist[fd].dirno = openfilelist[curdir].first;

    openfilelist[fd].diroff = i;

    strcpy(openfilelist[fd].dir, openfilelist[curdir].dir);

    strcat(openfilelist[fd].dir, filename);

    if(fcbptr->attribute & 0x20)

        strcat(openfilelist[fd].dir, "\\");

    openfilelist[fd].father = curdir;

    openfilelist[fd].count = 0;

    openfilelist[fd].fcbstate = 0;

    openfilelist[fd].topenfile = 1;

    return fd;

}

int my_close(int fd)

{

    fcb *fcbptr;

    int father;

    if(fd < 0 || fd >= MAXOPENFILE)

    {

        printf("Error,the file is not exist.\n");

        return -1;

    }

    if(openfilelist[fd].fcbstate)

    {

        fcbptr = (fcb *)malloc(sizeof(fcb));

        strcpy(fcbptr->filename, openfilelist[fd].filename);

        strcpy(fcbptr->exname, openfilelist[fd].exname);

        fcbptr->attribute = openfilelist[fd].attribute;

        fcbptr->time = openfilelist[fd].time;

        fcbptr->date = openfilelist[fd].date;

        fcbptr->first = openfilelist[fd].first;

        fcbptr->length = openfilelist[fd].length;

        fcbptr->free = openfilelist[fd].free;

        father = openfilelist[fd].father;

        openfilelist[father].count = openfilelist[fd].diroff * sizeof(fcb);

        do_write(father, (char *)fcbptr, sizeof(fcb), 2);

        free(fcbptr);

        openfilelist[fd].fcbstate = 0;

    }

    strcpy(openfilelist[fd].filename, "");

    strcpy(openfilelist[fd].exname, "");

    openfilelist[fd].topenfile = 0;

    return father;

}

int my_write(int fd)

{

    fat *fat1, *fat2, *fatptr1, *fatptr2;

    int wstyle, len, ll, tmp;

    char text[MAXTEXT];

    unsigned short blkno;

    fat1 = (fat *)(myvhard + BLOCKSIZE);

    fat2 = (fat *)(myvhard + 3 * BLOCKSIZE);

    if(fd < 0 || fd >= MAXOPENFILE)

    {

        printf("The file is not exist!\n");

        return -1;

    }

    while(1)

    {

        printf("Please enter the number of write style:\n1.cut write\t2.cover write\t3.add write\n");

        scanf("%d", &wstyle);

        if(wstyle > 0 && wstyle < 4)

            break;

        printf("Input Error!");

    }

    getchar();

    switch(wstyle)

    {

        case 1://截断写把原文件所占的虚拟磁盘空间重置为1

            blkno = openfilelist[fd].first;

            fatptr1 = fat1 + blkno;

            fatptr2 = fat2 + blkno;

            blkno = fatptr1->id;

            fatptr1->id = END;

            fatptr2->id = END;

            while(blkno != END)

            {

                fatptr1 = fat1 + blkno;

                fatptr2 = fat2 + blkno;

                blkno = fatptr1->id;

                fatptr1->id = FREE;

                fatptr2->id = FREE;

            }

            openfilelist[fd].count = 0;

            openfilelist[fd].length = 0;

            break;

        case 2:

            openfilelist[fd].count = 0;

            break;

        case 3:

            openfilelist[fd].count = openfilelist[fd].length;

            break;

        default:

            break;

    }

    ll = 0;

    printf("please input write data(end with Ctrl+Z):\n");

    while(gets(text))

    {

        len = strlen(text);

        text[len++] = '\n';

        text[len] = '\0';

        tmp = do_write(fd, text, len, wstyle);

        if(tmp != -1)

            ll += tmp;

        if(tmp < len)

        {

            printf("Wirte Error!");

            break;

        }

    }

    return ll;//实际写的字节数

}


int do_write(int fd, char *text, int len, char wstyle)

{

    fat *fat1, *fat2, *fatptr1, *fatptr2;

    unsigned char *buf, *blkptr;

    unsigned short blkno, blkoff;

    int i, ll;

    fat1 = (fat *)(myvhard + BLOCKSIZE);

    fat2 = (fat *)(myvhard + 3 * BLOCKSIZE);

    buf = (unsigned char *)malloc(BLOCKSIZE);

    if(buf == NULL)

    {

        printf("malloc failed!\n");

        return -1;

    }

    blkno = openfilelist[fd].first;

    blkoff = openfilelist[fd].count;

    fatptr1 = fat1 + blkno;

    fatptr2 = fat2 + blkno;

    while(blkoff >= BLOCKSIZE)

    {

        blkno = fatptr1->id;

        if(blkno == END)

        {

            blkno = findblock();

            if(blkno == -1)

            {

                free(buf);

                return -1;

            }

            fatptr1->id = blkno;

            fatptr2->id = blkno;

            fatptr1 = fat1 + blkno;

            fatptr2 = fat2 + blkno;

            fatptr1->id = END;

            fatptr2->id = END;

        }

        else

        {

            fatptr1 = fat1 + blkno;

            fatptr2 = fat2 + blkno;

        }

        blkoff = blkoff - BLOCKSIZE;//让blkoff定位到文件最后一个磁盘块的读写位置

    }


    ll = 0;//实际写的字节数

    while(ll < len)//len是用户输入的字节数

    {

        blkptr = (unsigned char *)(myvhard + blkno * BLOCKSIZE);

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

            buf[i] = blkptr[i];

        for(;blkoff < BLOCKSIZE; blkoff++)

        {

            buf[blkoff] = text[ll++];

            openfilelist[fd].count++;

            if(ll == len)

                break;

        }

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

            blkptr[i] = buf[i];

        if(ll < len)//如果一个磁盘块写不下,则再分配一个磁盘块

        {

            blkno = fatptr1->id;

            if(blkno == END)

            {

                blkno = findblock();

                if(blkno == -1)

                    break;

                fatptr1->id = blkno;

                fatptr2->id = blkno;

                fatptr1 = fat1 + blkno;

                fatptr2 = fat2 + blkno;

                fatptr1->id = END;

                fatptr2->id = END;

            }

            else

            {

                fatptr1 = fat1 + blkno;

                fatptr2 = fat2 + blkno;

            }

            blkoff = 0;

        }

    }

    if(openfilelist[fd].count > openfilelist[fd].length)

        openfilelist[fd].length = openfilelist[fd].count;

    openfilelist[fd].fcbstate = 1;

    free(buf);

    return ll;

}

int my_read(int fd, int len)

{

    char text[MAXTEXT];

    int ll;

    if(fd < 0 || fd >= MAXOPENFILE)

    {

        printf("The File is not exist!\n");

        return -1;

    }

    openfilelist[fd].count = 0;

    ll = do_read(fd, len, text);//ll是实际读出的字节数

    if(ll != -1)

        printf("%s", text);

    else

        printf("Read Error!\n");

    return ll;

}

int do_read(int fd, int len, char *text)

{

    fat *fat1, *fatptr;

    unsigned char *buf, *blkptr;

    unsigned short blkno, blkoff;

    int i, ll;

    fat1 = (fat *)(myvhard + BLOCKSIZE);

    buf = (unsigned char *)malloc(BLOCKSIZE);

    if(buf == NULL)

    {

        printf("malloc failed!\n");

        return -1;

    }

    blkno = openfilelist[fd].first;

    blkoff = openfilelist[fd].count;

    if(blkoff >= openfilelist[fd].length)

    {

        puts("Read out of range!");

        free(buf);

        return -1;

    }

    fatptr = fat1 + blkno;

    while(blkoff >= BLOCKSIZE)//blkoff为最后一块盘块剩余的容量

    {

        blkno = fatptr->id;

        blkoff = blkoff - BLOCKSIZE;

        fatptr = fat1 + blkno;

    }

    ll = 0;

    while(ll < len)

    {

        blkptr = (unsigned char *)(myvhard + blkno * BLOCKSIZE);

        for(i = 0; i < BLOCKSIZE; i++)//将最后一块盘块的内容读取到buf中

            buf[i] = blkptr[i];

        for(; blkoff < BLOCKSIZE; blkoff++)

        {

            text[ll++] = buf[blkoff];

            openfilelist[fd].count++;

            if(ll == len || openfilelist[fd].count == openfilelist[fd].length)

                break;

        }

        if(ll < len && openfilelist[fd].count != openfilelist[fd].length)

        {

            blkno = fatptr->id;

            if(blkno == END)

                break;

            blkoff = 0;

            fatptr = fat1 + blkno;

        }

    }

    text[ll] = '\0';

    free(buf);

    return ll;

}

void my_exitsys()

{

    FILE *fp;

    while(curdir)

        curdir = my_close(curdir);

    fp = fopen(myfilename, "w");

    fwrite(myvhard, SIZE, 1, fp);

    fclose(fp);

    free(myvhard);

}

unsigned short findblock()

{

    unsigned short i;

    fat *fat1, *fatptr;

    fat1 = (fat *)(myvhard + BLOCKSIZE);

    for(i = 7; i < SIZE / BLOCKSIZE; i++)

    {

        fatptr = fat1 + i;

        if(fatptr->id == FREE)

            return i;

    }

    printf("Error,Can't find free block!\n");

    return -1;

}


int findopenfile()

{

    int i;

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

    {

        if(openfilelist[i].topenfile == 0)

            return i;

    }

    printf("Error,open too many files!\n");

    return -1;

}

int main()

{

    char cmd[15][10] = {"cd", "mkdir", "rmdir", "ls", "create", "rm", "open", "close", "write", "read", "exit"};

    char s[30], *sp;

    int cmdn, flag = 1, i;

    startsys();

    printf("*********************File System V1.0*******************************\n\n");

    printf("命令名\t\t命令参数\t\t命令说明\n\n");

    printf("cd\t\t目录名(路径名)\t\t切换当前目录到指定目录\n");

    printf("mkdir\t\t目录名\t\t\t在当前目录创建新目录\n");

    printf("rmdir\t\t目录名\t\t\t在当前目录删除指定目录\n");

    printf("ls\t\t无\t\t\t显示当前目录下的目录和文件\n");

    printf("create\t\t文件名\t\t\t在当前目录下创建指定文件\n");

    printf("rm\t\t文件名\t\t\t在当前目录下删除指定文件\n");

    printf("open\t\t文件名\t\t\t在当前目录下打开指定文件\n");

    printf("write\t\t无\t\t\t在打开文件状态下,写该文件\n");

    printf("read\t\t无\t\t\t在打开文件状态下,读取该文件\n");

    printf("close\t\t无\t\t\t在打开文件状态下,读取该文件\n");

    printf("exit\t\t无\t\t\t退出系统\n\n");

    printf("*********************************************************************\n\n");

    while(flag)

    {

        printf("%s>", openfilelist[curdir].dir);

        gets(s);

        cmdn = -1;

        if(strcmp(s, ""))

        {

            sp=strtok(s, " ");

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

            {

                if(strcmp(sp, cmd[i]) == 0)

                {

                    cmdn = i;

                    break;

                }

            }

            // printf("%d\n", cmdn);

            switch(cmdn)

            {

                case 0:

                    sp = strtok(NULL, " ");

                    if(sp && (openfilelist[curdir].attribute & 0x20))

                        my_cd(sp);

                    else

                        printf("Please input the right command.\n");

                    break;

                case 1:

                    sp = strtok(NULL, " ");

                    if(sp && (openfilelist[curdir].attribute & 0x20))

                        my_mkdir(sp);

                    else

                        printf("Please input the right command.\n");

                    break;

                case 2:

                    sp = strtok(NULL, " ");

                    if(sp && (openfilelist[curdir].attribute & 0x20))

                        my_rmdir(sp);

                    else

                        printf("Please input the right command.\n");

                    break;

                case 3:

                    if(openfilelist[curdir].attribute & 0x20)

                        my_ls();

                    else

                        printf("Please input the right command.\n");

                    break;

                case 4:

                    sp = strtok(NULL, " ");

                    if(sp && (openfilelist[curdir].attribute & 0x20))

                        my_create(sp);

                    else

                        printf("Please input the right command.\n");

                    break;

                case 5:

                    sp = strtok(NULL, " ");

                    if(sp && (openfilelist[curdir].attribute & 0x20))

                        my_rm(sp);

                    else

                        printf("Please input the right command.\n");

                    break;

                case 6:

                    sp = strtok(NULL, " ");

                    if(sp && (openfilelist[curdir].attribute & 0x20))

                    {

                        if(strchr(sp, '.'))//查找sp中'.'首次出现的位置

                            curdir = my_open(sp);

                        else

                            printf("the openfile should have exname.\n");

                    }

                    else

                        printf("Please input the right command.\n");

                    break;

                case 7:

                    if(!(openfilelist[curdir].attribute & 0x20))

                        curdir = my_close(curdir);

                    else

                        printf("No files opened.\n");

                    break;

                case 8:

                    if(!(openfilelist[curdir].attribute & 0x20))

                        my_write(curdir);

                    else

                        printf("No files opened.\n");

                    break;

                case 9:

                    if(!(openfilelist[curdir].attribute & 0x20))

                        my_read(curdir, openfilelist[curdir].length);

                    else

                        printf("No files opened.\n");

                    break;

                case 10:

                    if(openfilelist[curdir].attribute & 0x20)

                    {

                        my_exitsys();

                        flag = 0;

                    }

                    else

                        printf("Please input the right command.\n");

                    break;

                default:

                    printf("Please input the right command.\n");

                    break;

            }

        }

    }

    return 0;

}