be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端C++实现的Windows系统的二级文件系统

Palpitation

发布日期: 2019-10-27 11:00:56 浏览量: 100
评分:
star star star star star star star star star star_border
*转载请注明来自write-bug.com

一、需求分析

1.1 程序任务

本程序的任务是以一个二进制文件为文件卷,建立一个be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端Windows系统的二级文件系统。用户使用可执行程序,可以输入程序提供的指令进行文件管理操作,具体包括创建文件、打开文件、读取文件、写入文件、删除文件、定位文件指针、列目录、格式化文件卷等。相应的输出形式为文件句柄、读取写入字节数等结果。

二、概要设计

2.1 任务分解

设计二级文件系统的任务主要可以分解为磁盘结构定义、Superblock、inode和Disknode的数据结构定义、文件操作接口定义、文件操作接口实现以及用户交互设计。

2.2 数据类型

本程序采用C++语言实现,将磁盘Superblock、inode和Disknode全部采用结构体实现。整个虚拟文件卷共占87个物理盘块,Superblock占1个,INode区占10个,DiskNode区占76个。

Superblock的结构体定义如下:

  1. //文件系统控制块,大小固定为200+4+304+4=512B,即一个物理盘块
  2. struct SuperBlock
  3. {
  4. int freeINode[50]; //管理空闲INode,保存空闲INode编号
  5. int numOfFreeINode; //剩余空闲INode数量
  6. int freeDiskNode[76]; //管理空闲DiskNode,保存空闲DiskNode编号
  7. int numOfFreeDiskNode; //剩余空闲DiskNode数量
  8. };

Inode的结构体定义如下:

  1. //文件控制块,每个文件对应一个INode,每一个INode块占64B
  2. typedef struct INode
  3. {
  4. char filename[28]; //文件名
  5. int index; //INode块编号
  6. int addr[5]; //文件索引表,采用二级索引,存放逻辑块号对应的物理块号
  7. int mode; //文件打开模式,分为只读、只写、可读写,即0、1、2
  8. int file_type; //文件类型,分为普通文件和目录文件,对应0、1
  9. int numOfSubPath; //子目录/文件个数
  10. }INode, INodes[numOfInode];

Disknode的结构体定义如下:

  1. //物理盘块,一个盘块512B
  2. typedef struct DiskNode
  3. {
  4. char content[512];
  5. }DiskNode, DiskNodes[numOfDisknode];

除了上述三个结构体,本程序还定义了如文件类型、文件访问权限等宏定义如下所示:

  1. #define numOfInode 80
  2. #define numOfDisknode 76
  3. #define read_only 0
  4. #define write_only 1
  5. #define read_write 2
  6. #define dirfile 1
  7. #define normalfile 0

2.3 主程序流程

主程序流程图如下:

2.4 模块间调用关系

单向箭头表示出射模块调用了入射模块。

主模块是main函数,在main函数中首先调用InitFileSystem模块从disk文件卷中读取数据,赋值给对应数据结构。随后调用InputCommand模块与用户进行交互,执行用户要求的命令。用户要求的命令包括创建文件,打开文件,读取文件,写文件,移动文件指针和删除文件等。创建文件和打开文件都会调用SearchPath这个核心模块。删除文件会调用RetrieveINode模块和RetrieveDiskNode模块来回收Inode节点和文件占用的物理盘块。

三、详细设计

3.1 重点函数

  1. int SearchPath(string filename)

参数为文件名,若文件存在,则返回文件的INode节点的编号(从1开始)。

函数首先使用split(filepath, “/“)函数对文件名以/为分隔符进行分割,得到各级目录文件的文件名。以根目录root为起点(根目录文件的INode编号固定为1),以类似UNIXV6++的目录检索方式,以32个字节为一个目录项,前28字节为文件名,后4个字节为对应文件的INode编号。这样一级一级地检索,直到找到文件的INode节点。如果路径存在错误或者找不到该文件,则函数返回-1。

  1. int createFile(string filename, int file_type, int mode)

该函数用于创建文件。参数为文件名,文件类型和文件读写权限,返回值为文件INode节点的编号,同时也是文件句柄。函数首先调用SearchPath模块搜索文件是否已经存在,如果文件已经存在则返回文件的INode节点编号。如果文件不存在,再检查是否还有空闲INode节点,如果没有则返回-1。否则再次调用SearchFile模块找到父目录的INode节点,根据父目录INode节点的inodes[parent_index - 1].numOfSubPath变量确定新目录项插入的位置,然后将新申请的INode节点编号和文件名存入目录项。如果父目录已经达到最大文件数量,则无法创建,返回-1。

  1. int openFile(string filename, int mode)

该函数用于打开文件。参数为文件名以及文件打开方式。函数首先调用SearchPath模块查找文件对应的INode编号,如果文件不存在或者打开方式不符合权限则返回-1。否则在用户文件打开表中查找文件是否已经被打开,如果已打开直接返回句柄,否则更新UOT,再返回。

  1. //用户打开文件表
  2. typedef struct UOT
  3. {
  4. int list[10];
  5. int numOfOpenedFile = 0;
  6. int offset[10] = { 0 };
  7. }UOT;
  1. int readFile(int index, char* buffer, int length)

该函数用于从文件读取数据到指定数组。参数为文件句柄(INode节点编号),数组首地址以及欲读取的字节数。返回值为实际读取的字节数。函数首先检查用户打开文件表,查看文件是否已经打开。如果未打开,则返回-1.如果已打开,则检查文件是否为只读或者可读写文件,如果不是则返回-1。如果已打开且满足读写权限,则根据该文件文件指针确定读起始地址,定义相关变量记录剩余待读取字节数、数组偏移等。读取完毕后,更新文件指针位置,返回实际读取的字节数。

  1. int left = length;
  2. int buffer_offset = 0;
  3. //全部读取完毕
  4. if (left == 0)
  5. {
  6. cout << "读取成功......" << endl;
  7. uot.offset[a] += length;
  8. return length;
  9. }
  10. //读取部分内容
  11. else
  12. {
  13. cout << "读取成功......" << endl;
  14. uot.offset[a] += (length - left);
  15. return (length - left);
  16. }
  1. int writeFile(int index, const char* buffer, int length)

该函数用于从指定数组写入length字节到文件中。参数为文件句柄,数组首地址以及欲写入的字节数。函数首先检查用户打开文件表,查看文件是否已经打开。如果未打开,则返回-1.如果已打开,则检查文件是否为只写或者可读写文件,如果不是则返回-1。如果已打开且满足读写权限,则根据该文件文件指针确定写起始地址,定义相关变量记录剩余待写入字节数、数组偏移等。写入完毕后,更新文件指针位置,返回实际写入的字节数。实际写入的字节数可能因为文件剩余磁盘空间不足而小于length。

  1. int left = length;
  2. int buffer_offset = 0;
  3. //数据全部写入文件
  4. if (left == 0)
  5. {
  6. cout << "写入成功......" << endl;
  7. uot.offset[a] += length;
  8. return length;
  9. }
  10. //超出文件尾部,只写入了部分
  11. else if (k >= 5)
  12. {
  13. cout << "写入成功,写入部分数据......" << endl;
  14. uot.offset[a] += (length - left);
  15. return (length - left);
  16. }
  1. int seekPos(int index, int offset)

该函数用于定位文件读写指针。参数为文件句柄以及文件读写指针偏移量。函数首先查看用户打开文件表,如果文件已打开,则直接修改读写指针位置,返回1表示成功。如果未打开,则返回0表示失败。

  1. bool deleteFile(string filename)

该函数用于删除指定文件。参数为文件名(包含完整路径)。函数首先调用SearchPath模块查找文件名对应的INode节点,如果文件不存在,则返回false。如果文件存在,则调用RetrieveDiskNode模块回收物理盘块,调用RetrieveINode模块回收INode节点。接着再次调用SearchPath模块查找文件父目录文件INode节点,接着从父目录文件的第一个物理盘块开始以32个字节为单位取出目录项进行文件名的匹配,如果匹配成功则将最后一个目录项的32字节内容覆盖到该32字节上,然后将原本最后一个目录项的32字节设置为全0。

查找父目录INode:

  1. string parent_path = filename.substr(0, filename.length() - name.length() - 1);
  2. int parent_index = SearchPath(parent_path);

更新父目录:

  1. int k = inodes[parent_index - 1].numOfSubPath / (512 / 32);
  2. int m = inodes[parent_index - 1].numOfSubPath % (512 / 32);
  3. if (m > 0)
  4. {
  5. memcpy(disknodes[inodes[parent_index - 1].addr[i] - 1].content + j * 32, disknodes[inodes[parent_index - 1].addr[k] - 1].content + (m - 1) * 32, 32);
  6. memset(disknodes[inodes[parent_index - 1].addr[k] - 1].content + (m - 1) * 32, 0, 32);
  7. }
  8. else
  9. {
  10. memcpy(disknodes[inodes[parent_index - 1].addr[i] - 1].content + j * 32, disknodes[inodes[parent_index - 1].addr[k - 1] - 1].content + 15 * 32, 32);
  11. memset(disknodes[inodes[parent_index - 1].addr[k - 1] - 1].content + 15 * 32, 0, 32);
  12. }
  13. break;

3.2 函数间调用关系

函数间调用关系类似于模块间调用关系,见概要设计。

四、调试分析

4.1 测试及测试结果

测试数据

在根目录root下创建RSY普通文件,从文件开头写入字符串12345,共5个字节。

测试结果

测试数据

从RSY文件的第二个字节开始,读取4个字节的内容。

测试结果

测试数据

列目录。

测试结果

测试数据

删除RSY文件,然后列目录。

测试结果

4.2 复杂度分析

程序中涉及的复杂度分析主要是针对SearchPath模块,INode节点和DiskNode节点的分配与回收。

SearchPath模块中,假设文件路径长度为n,那么根据其搜索时的线性性质,时间复杂度为O(n)。

INode节点和DiskNode的分配与回收算法采用的方式是栈。通过出栈和入栈来进行节点的分配与回收,所以时间复杂度为O(1)。

4.3 问题思考

在设计SearchPath模块时,需要将完整的文件路径进行拆分,拆分成从root根目录文件到文件本身的各级目录文件但,C++本身没有库函数能够根据指定分隔符拆分字符串。问题的解决是通过在网上查阅资料,查到利用C++标准库的vector容器和string类实现分割的函数,并加以利用。

在设计deleteFile模块时,需要更新删除文件父目录文件的目录项。原本的思路是将删除文件的目录项后的其他剩余目录项依次向前覆盖,但实现时发现这样的思路太过复杂而且费时,代码冗长。问题的解决是我使用了另一种思路,即直接通过在目录文件INode节点中添加numOfSubpath变量记录当前包含了多少个子文件,然后直接将最后一个目录项的内容覆盖到删除文件的目录项中,再将原本最后一个目录项全置为0。

五、用户使用说明

本程序开发平台为Win10操作系统,可运行在Windows系统之上。本程序以命令行界面为交互界面,用户启动可执行文件之后会进入命令行界面。根据界面上的操作提示,用户输入想要执行的操作编号,根据提示进行输入。界面如下:

六、实验总结

这次操作系统课程设计实验的目标是设计实现一个二级文件系统。在经过了一个学期的操作系统课程的学习,我了解了UNIXV6++操作系统的文件系统是如何设计的,所以整个二级文件系统的思路与UNIXV6++的文件系统类似。

实验一开始也遇到一些问题,比如不知道该如何利用Windows系统上的一个二进制文件作为虚拟文件卷,后来通过查阅、回忆之前C++课程中的文件相关操作和内存管理函数,解决了这个问题。在具体实现创建、打开以及读写等文件操作接口时反而并没有遇到太大的困难,更多的是需要考虑到所有的特殊情况。

上传的附件 cloud_download be365体育在线投注_365BET能赢钱吗_手机365体育投注客户端C++实现的Windows系统的二级文件系统.7z ( 698.53kb,?2次下载 )
error_outline 下载需要11点积分
eject