nachos实验——文件系统实现

2023-10-24 18:58

本文主要是介绍nachos实验——文件系统实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.     实验内容

直接引用实验手册(这学期的助教真的贴心):

 

NachOS 文件系统简介

在 NachOS 里,有两份文件系统的实现:

  • 利用宿主机的 File System 接口实现 NachOS 文件操作,直接读写宿主操作系统上的文件
  • 维护一个宿主机上的DISK文件作为 NachOS 的模拟磁盘,在其上进行基于 Sector 的操作

在lab2代码的基础上,将code/build.linux/Makefile line 194 中的 -DFILESYS_STUB去掉,并将code/filesys/filesys.h中的 Line 67 ~ 94 的Write()Read()两个函数定义拷至该文件后半部分的class FileSystem定义中,重新编译即可使用内置的虚拟磁盘:

$ cd code/build.linux
$ make clean
$ make -j
$ cd ../test
$ ../build.linux/nachos -f
$ ../build.linux/nachos -cp fork fork_in_virtual_disk
$ ../build.linux/nachos -x fork_in_virtual_dist
cd code/build.linux
$ make clean
$ make -j
$ cd ../test
$ ../build.linux/nachos -f
$ ../build.linux/nachos -cp fork fork_in_virtual_disk
$ ../build.linux/nachos -x fork_in_virtual_dist

其中,-f参数将格式化 NachOS 虚拟磁盘,-cp将文件从 UNIX 文件系统拷贝到 NachOS 文件系统中,-x参数使 NachOS 在虚拟磁盘中寻找对应名字的可执行文件并执行。更多参数可以参照code/threads/main.cc中的注释

实验内容与要求

NachOS 自带的文件系统限制诸多,例如:不支持变长文件;不支持目录;不支持多级索引。

本次实验分为两个阶段:

  • Part 1:多级索引
  • Part 2:多级目录与数据恢复

在本次实验中,你需要对助教提供的 Lab3 部分代码进行完善,对 NachOS 文件系统进行功能扩充。

我们提供了两个阶段的完整代码,需要将已完成的阶段一的代码复制到新项目的相应位置(将完成的6个洞的代码复制到对应位置即可)。

Part1 多级索引

主要任务

你需要阅读lab3源码中的如下文件:

- <root>| NachOS-4.0| code| filesys
>>      | filehdr.cc
>>      | filehrd.h
>>      | filesys.cc
>>      | filesys.h
>>      | openfile.cc
>>      | openfile.h
>>      | synchdisk.cc| 其他已有的文件

与原版NachOS进行比对,理解NachOS一级索引的工作流程。同时仔细阅读提供的实验代码的注释,思考多级索引与一级索引的不同,并根据提示完成注释中提示的空缺部分

重点与难点

  • 当写文件的长度超过文件现有长度,如何对文件进行扩充?
  • 多级索引文件偏移如何映射到实际的sector号?

测试方法

$ cd code/test
$ ./toTestFileSys.sh > testOutcome.txt; cat testOutcome.txt
$ # toTestFileSys脚本内容如下:
$ nl toTestFileSys.sh 1	#!/bin/bash2	nachos='../build.linux/nachos'3	$nachos -f4	$nachos -cp h.txt h.txt5	$nachos -cp testFileSys testFileSys6	$nachos -x testFileSyscd code/test
$ ./toTestFileSys.sh > testOutcome.txt; cat testOutcome.txt
$ # toTestFileSys脚本内容如下:
$ nl toTestFileSys.sh 1	#!/bin/bash2	nachos='../build.linux/nachos'3	$nachos -f4	$nachos -cp h.txt h.txt5	$nachos -cp testFileSys testFileSys6	$nachos -x testFileSys

正确的测试结果见主页的testOutcome.txt

Part2 多级目录与数据恢复

主要任务

阶段二相较于阶段一,主要是下面的若干文件发生了改动:

- <root>| NachOS-4.0| code| filesys
>>      | directory.cc
>>      | directory.h
>>      | filesys.cc

理解NachOS创建根目录的工作流程,同时仔细阅读提供的实验代码的注释,思考如何创建一个新目录,以及如何恢复已删除数据,并根据提示完成注释中提示的空缺部分。

  • 多级目录:

    NachOS采用特殊的文件来表示一个目录,该特殊文件采用固定大小,文件用于存放固定个数的目录项(DirectoryEntry),NachOS中创建一个目录就是创建一个相应的特殊文件。

  • 数据恢复:

    NachOS通过FileSystem::Remove函数来删除一个文件,该函数的作用是将已分配给该文件的扇区释放,然后在当前目录中将该文件的相应目录项的inUse字段置为FALSE。可以看到,删除文件后仍残留着一些信息可以供我们做数据恢复。为了避免恢复时覆盖丢失数据,我们将恢复出的数据写入到原生Linux系统中的一个文件中。

重点与难点

  • 如何在一个目录中创建一个新的目录?
  • NachOS删除一个文件时做了哪些操作?有哪些残留信息可以帮助我们恢复文件?

测试方法

$ cd code/test
$ ./toTestDirectoryAndRecovery.sh;
$  cat recovery.txt
cd code/test
$ ./toTestDirectoryAndRecovery.sh;
$  cat recovery.txt

toTestDirectoryAndRecovery.sh脚本通过创建文件folder1/folder2/file,对其进行读写来验证多级目录相关代码的正确性,随后通过删除该文件,再恢复到recovery.txt中来验证recovery功能是否正常。若recovery.txt中包含10行字符串we write contents to folder1/folder2/file则说明recovery功能是正常的。

详细培训-那么我们需要干嘛呢??

多级索引

索引是一种将文件内容组织在磁盘上的方式。

当用户想要访问一个指定文件的指定偏移时,需要查找索引表,然后根据索引表的提供的sector号去访问文件内容。

  • 一级索引:

    在文件Header中有一个sector号的数组。

    由于每个sector大小固定,访问指定偏移时,只需要将偏移除以sector大小即可知道sector号在数组第几个,根据该sector号去这个sector上获取文件内容。该sector称为dataSector

     

    这样带来的好处是文件不用连续存放,可以分散在磁盘各个位置,提高利用率,并且访问每次访问指定偏移所需的读取磁盘的次数恒定为2(访问header算一次)。

    坏处则十分显然:文件最大大小受到header大小的限制

  • 二级索引:

    相比一级索引,二级索引就是将sector号数组放在磁盘上,而不是放在header中。这个存放sector号数组的sector称为indiretSector

    访问指定偏移时,首先看该偏移是否在一级索引可以寻找到的位置,若不在,则计算sector号其应该在哪一个indirectSector,在其中获取sector号数组,接下来类似一级索引

     

你需要做的-补洞!

首先,如果你用grep(global search regular expression(RE) and print out the line)命令匹配"洞",就会得到:


所以,这次实验一共有11个(阶段一6个+阶段二5个)"洞"需要勇敢的你来填补!
阶段一的6个洞按照顺序分别在这六个成员函数中.

FileHeader::Allocate()
FileHeader::Deallocate()
FileHeader::ByteToSector()
FileHeader::expandFile()
OpenFile::WriteAt()
FileSystem::Open()

阶段二的5个洞按照顺序分别在这五个成员函数中(其中有三个洞的需要填的内容基本一样).

 


FileSystem::Create()
FileSystem::CreateFolder()
FileSystem::Open()
FileSystem::Remove()
FileSystem::Recover()

等等!

在补洞之前

当然了,在开始补洞之前,你还需要理解这个洞,也就要阅读相关源码.
这次你需要关注的lab3相关源码是哪些呢?也许聪明的你已经想到了,就是这些:

如果你用grep递归匹配lab\s?3(这是正则表达式,以后如果掌握了可能大大提高字符串匹配效率),就会得到如下结果:

 

你可以参照前几次实验同样位置的源码,理解清楚本次多级索引在此基础上增加了哪些工作量

补洞

下面开始给出进一步的提示:

  • 洞1:FileHeader::Allocate
    首先,你需要对indirectSectors进行处理,方法类似于:
    dataSectors[i] = freeMap->FindAndSet();
    然后,对sectors[NumInDirectIndex]数组元素一次进行类似操作,注意继续统计doneSec,直到这次的sectors[]用完,或者doneSec达到numSectors的大小。
    最后,你需要将sectors[],用SynchDisk中的某接口写入到indirectSectors[j]中。
  • 洞2:FileHeader::Deallocate
    和洞1很相似,这里为了deallocate,首先要读indirectSectors[j] (用什么接口呢?),然后进行allocate的逆操作,然后将对应的使用了的sector进行clear操作,最后别忘了indirectSectors[j]也要clear
  • 洞3:FileHeader::ByteToSector
    返回offset这个字节所在的sector号。
    分为在direct sector和在indirect sector两种情况。这里需要理解多级索引的地址计算,加油!
  • 洞4:FileHeader::expandFile
    在write的文件大小超过原本大小的时候变长。找到indirectSectors[j]!=-1时的sector,或者indirectSectors[j]==-1的第一个sector,并进行修改,修改的操作和allocate洞1十分相似,别忘了将修改的secetors[]写回到indirectSectors[j]的块中。
  • 洞5:OpenFile::WriteAt
    先计算需要的sector总数目,然后调用FileHeader类里的expandFile()函数。
    之后修改hdr,也就是写回(FileHeader::WriteBack),最后写回freeMap
  • 洞6:FileSystem::Open
    这里处理open。
    根据得到的sector,添加到openedFile里面。
    注意不要修改openedFile[0,1,2],从openedFile[3]开始分配
  • 洞7:FileSystem::Create(char *name, int initialSize)
    将name包含的目录名解析出来,然后从根目录开始跳转定位到当前目录。
    若name中包含的目录不存在,则认为失败并返回FALSE。
    例如,name为“folder1/folder2/file1”,则需要从根目录出发,通过其下的folder1到达folder2,然后在folder2目录下创建文件file1,若folder1或folder2不存在,则失败返回FALSE。
  • 洞8:FileSystem::CreateFolder(char *name)
    将name包含的目录名解析出来,然后从根目录开始跳转定位到当前目录,并在当前目录下创建相应的新目录,具体过程可参考NachOS的根目录创建过程。
    例如,name为“folder1/folder2/folder3”,则需要从根目录出发,通过其下的folder1到达folder2,然后在folder2目录下创建名为folder3的文件(目录就是一个文件),若folder1或folder2不存在,则失败返回FALSE。
    注:实现该函数可以参考FileSystem::Create函数。
  • 洞9:FileSystem::Open(char *name)
    将name包含的目录名解析出来,然后从根目录开始跳转定位到当前目录。
    若name中包含的目录不存在,则认为失败并返回FALSE。
  • 洞10:FileSystem::Remove(char *name)
    将name包含的目录名解析出来,然后从根目录开始跳转定位到当前目录。
    若name中包含的目录不存在,则认为失败并返回FALSE。
  • 洞11:FileSystem::Recover(char *srcName, char *dstName)
    将srcName包含的目录名解析出来,然后从根目录开始跳转定位到当前目录,并在当前目录下判断是否能够恢复(即目录表项是否还有条目的名字为待恢复文件的名字),若能的话,恢复相应的文件并存放到原生Linux系统下的dstName文件中。
    注:实现该函数可以参考FileSystem::Create函数。

 

 

 

2.     具体实现

1)     part1——多级索引的实现

a)多级索引的原理

        在文件Header中有一个sector号的数组。由于每个sector大小固定,访问指定偏移时,只需要将偏移除以sector大小即可知道sector号在数组第几个,根据该sector号去这个sector上获取文件内容。该sector称为dataSector

        二级索引就是将sector号数组放在磁盘上,而不是放在header中。这个存放sector号数组的sector称为indiretSector。

           我们在本实验中只考虑二级索引,多级索引其实可以类推。

b)代码分析

主要涉及filehdr.cc、openfile.cc和filesys.cc,补充或者修改了以下函数:

FileHeader::Allocate()

FileHeader::Deallocate()

FileHeader::ByteToSector()

FileHeader::expandFile()

OpenFile::WriteAt()

FileSystem::Open()

洞1:这是在Allocate()的补充的一段代码,用来分配indiretSector,比较简单。

indirectSectors[j]=freeMap->FindAndSet();      int k;for(k=0; k<NumInDirectIndex&& doneSec < numSectors; k++,doneSec++){//关键在于这里边界条件,要兼顾两方面sectors[k]=freeMap->FindAndSet();//分配索引}if(doneSec==numSectors)for(intp=k;p<NumInDirectIndex;p++)sectors[p]=-1;kernel->synchDisk->WriteSector(indirectSectors[j],(char*)sectors);

洞2:这里代码位于Deallocate(),可以看作洞1的反向作用,用到了freemap->clea去清除相应sector的索引。代码就不贴了。

洞3:位于ByteToSector(),我们要做的是补充寻找offset对应byte所在的sector号的过程,这里的计算方法虽然简短但是值得说一下:

if(offset<DirectSize)  index=dataSectors[offset/SectorSize];//如果在datasector里,直接计算sector号else{//否则把前面计算的indirectIndex对应的次级sector的内容读到sectors里kernel->synchDisk->ReadSector(indirectSectors[indirectIndex],(char*)sectors);//然后找出具体的对应的sectorindex=sectors[(offset-DirectSize - indirectIndex*InDirectSectorSize)/SectorSize];}

 洞4:在expandFile()中,先说实现想法——对于只使用了部分的indirectsector,要把未使用的部分先扩展,其后才是扩展其他的indirectsector。

  int sectors[NumInDirectIndex]; //indextableint k;for(k=0;k<NumInDirectIndex;k++) sectors[k]=-1;if (indirectSectors[j] ==-1){indirectSectors[j]=freeMap->FindAndSet();for(k=0; k<NumInDirectIndex&& doneSec < numSec; k++,doneSec++){sectors[k]=freeMap->FindAndSet();}kernel->synchDisk->WriteSector(indirectSectors[j],(char*)sectors);}else{ // 有东西kernel->synchDisk->ReadSector(indirectSectors[j],(char*)sectors);for(k=0; k<NumInDirectIndex;k++){if(sectors[k] ==-1){if(doneSec < numSec)sectors[k]=freeMap->FindAndSet();elsebreak;}  }kernel->synchDisk->WriteSector(indirectSectors[j],(char*)sectors);}

 

洞5:位于WriteAt(),主要工作是对写入大小超出初始限度的情况进行处理。

 

          首先先修改头文件的信息,改变记录的大小。

hdr->SetBytes(position+numBytes);

hdr->WriteBack(getHeaderSector());

            然后是对文件进行扩展,这里主要是调用了上面的expandFile()。

 if((position+numBytes) > fileLength){OpenFile *freeMapFile = kernel->fileSystem->getFreeMapFile();PersistentBitmap *freemap =newPersistentBitmap(freeMapFile, NumSectors);int numSectors = ( position +numBytes +SectorSize -1)/SectorSize;if(hdr->expandFile(numSectors,freemap)){hdr->WriteBack(getHeaderSector());freemap->WriteBack(freeMapFile);fileLength = hdr->FileLength();}elsereturn0;delete freemap;} 

 

              洞6:这里的操作是将opened文件加入到队列之中,这里关键在于

 

   //  注意不要修改openedFile[0,1,2],openedFile[3]开始分配

关于这一点私以为是前面维护了打开的目录文件或者bitmap等文件。

for(i=3;i<MaxOpenFile;i++){OpenFile *file =openedFile[i];if(file ==NULL){  if(sector>=0)openFile =newOpenFile(sector);else    returnNULL;addFile(i,openFile);openFile->setId(i);break;}}

 

 

 

2)     part2——多级目录与数据恢复的实现

主要涉及filesys.cc的洞7~洞11,补充或者修改了以下函数:

FileSystem::Create()

FileSystem::CreateFolder()

FileSystem::Open()

FileSystem::Remove()

FileSystem::Recover()

 

洞7:在Create()函数里,这里我们要做的是根据解析的目录进行跳转跳转。

                int i;int hdr_sec;for (i =0; i < folderStrVec.size()-1; i++) {//c_str()返回一个指向正规C字符串的指针, 内容与本string串相同. 这是为了与c语言兼容  name =(char*)folderStrVec[i].c_str();//在当前目录中寻找下一级目录/文件hdr_sec = currentDirectory->Find(name);if(hdr_sec !=-1){//找到下级目录后更改当前目录currentDirectoryFile =newOpenFile(hdr_sec);currentDirectory->FetchFrom(currentDirectoryFile);}elsereturnFALSE;}

 

洞8:

 

boolFileSystem::CreateFolder(char*name){Directory *currentDirectory =newDirectory(NumDirEntries);OpenFile *currentDirectoryFile = directoryFile;currentDirectory->FetchFrom(currentDirectoryFile);//这里是目录解析的代码,实际上基本照抄FileSystem::Create里的解析方法即可,这里是洞中第一次出现,之后不会再分析vector<string> folderStrVec;//详细说一下strtok函数,我之前没有了解过这个字符串处理函数,其实功能蛮强大的,用于切割字符串,将str切分成一个个子串 在第一次被调用的时间str是传入需要被切割字符串的首地址;在后面调用的时间传入NULL。 delimiters:表示切割字符串(字符串中每个字符都会当作分割符)。 char* tmpStr =strtok(name,"/");while (tmpStr !=NULL){folderStrVec.push_back(string(tmpStr));tmpStr =strtok(NULL, "/");}……//这里是目录的跳转,代码基本同洞7,故略去//具体创建过程name = (char*) folderStrVec[i].c_str();PersistentBitmap *freemap =newPersistentBitmap(freeMapFile, NumSectors);int dir_sec = freemap->FindAndSet();//分配索引freemap->WriteBack(freeMapFile);//写回if(dir_sec){//申请成功FileHeader *dir_hdr =newFileHeader();if(dir_hdr->Allocate(freemap,DirectoryFileSize)){//分配空间freemap->WriteBack(freeMapFile);Directory *dir =newDirectory(NumDirEntries);//头文件写回dir_hdr->WriteBack(dir_sec);OpenFile *dir_file=newOpenFile(dir_sec);dir->WriteBack(dir_file);currentDirectory->Add(name,dir_sec);//添加到目录文件delete dir;delete dir_file;}elsereturnFALSE;}currentDirectory->WriteBack(currentDirectoryFile);if (currentDirectoryFile != directoryFile)delete currentDirectoryFile;returnTRUE;}

 

洞9洞10:目录的解析跳转,此不赘述。

 

洞11:recover

boolFileSystem::Recover(char*srcName, char*dstName){FILE *out =fopen(dstName, "w");Directory *currentDirectory =newDirectory(NumDirEntries);OpenFile *currentDirectoryFile = directoryFile;currentDirectory->FetchFrom(currentDirectoryFile);……//此处略去目录解析跳转srcName = (char*) folderStrVec[i].c_str();int srcSector = currentDirectory->Find(srcName,TRUE);//这里参数true设置很关键,否则将到不到对应的被移除文件if( srcSector==-1)returnFALSE;else{OpenFile *srcFile =newOpenFile(srcSector);FileHeader *hdr =srcFile->getHdr();//获得headerint length = hdr->GetBytes();//获得文件大小char str[length+5];srcFile->Read(str,length);//读取内容str[length]='\0';fwrite(str,1,length,out);//将读的内容保存到dstName对应文件里delete srcFile;}delete currentDirectory;if (currentDirectoryFile != directoryFile)delete currentDirectoryFile;fclose(out);returnTRUE;}

 

这里说一下前面提到的find的问题

 

Int Directory::Find(char*name,bool justCompareName)

这里会调用:

FindIndex(name, justCompareName);

然后观察这个函数如下

intDirectory::FindIndex(char*name, bool justCompareName){if (justCompareName) {for (inti =0; i < tableSize; i++)if (!strncmp(table[i].name, name, FileNameMaxLen))return i;return-1;      // name not in directory}for (inti =0; i < tableSize; i++)if (table[i].inUse&&!strncmp(table[i].name, name, FileNameMaxLen))//如果前面不设置成ture,这里还会确认sector是否被使用,然而移除的文件势必相应的inUse=0,这样就找不到文件位置了return i;return-1;      //name not in directory}

 

 

 

3.     遇到的问题

a)       一个问题就是没有注意到find的justcomparename参数的意义,设置不对,导致recover总是结果出错。

b)       刚开始自己上手直接写字符串处理,解析目录,结果繁琐还有bug,然后发现了其实给的环境里就有写好的解析目录的字符串处理……而且使用strtok代码十分简洁。

c)       在补洞4的是时候没有如下的赋初值操作

for(k=0;k<NumInDirectIndex;k++)sectors[k]=-1;

这样实际上就欠考虑,比如,如果一个indirectSector没有使用完,写回的时候,没有被赋值的索引的值就不正确。

 

这篇关于nachos实验——文件系统实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/277133

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import