Nachos操作系统-文件系统添加多级目录
多級目錄設置
前面提到了,當前的文件系統(tǒng)中并沒有完成對于多級目錄的設置。
為了更好的了解Nachos,這里嘗試向當前的文件系統(tǒng)中添加代碼以完成多級目錄的設置。
總覽
對于多級目錄來說,與其相關的操作主要有這幾個:
- 創(chuàng)建目錄
- 刪除目錄
- 向目錄中添加文件
- 從目錄中刪除文件
- 展示目錄及目錄中的文件內(nèi)容
為了便于代碼的編寫,我定義一個宏CntDirectLevel代表最多擁有的目錄層級
接下來對各自的實現(xiàn)進行設計與分析:
通用函數(shù)
在這里創(chuàng)建一個通用函數(shù)bool ParseFileName(char* name,char** &dirs),并將其存儲在單獨的目錄ParseFN.h中。
函數(shù)功能即判斷檔期按輸入的文件名是否在一個目錄下。如果是,返回TRUE,并將各級目錄名存儲在dirs中。如果不是,返回FALSE。
具體實現(xiàn)如下:
bool ParseFileName(char* name,char**& dirs) {if(name[0]=='.' && name[1]=='/'){dirs=new char*[10];int DirLevInd=0;for(int i=1;name[i]!='\0';){ //check the '/'if(name[i]=='/'){cout<<i<<endl;i++;int index=0;dirs[DirLevInd]=new char[10];// get the name of directory[DirLevInd-1]for(;name[i]!='\0' && name[i]!='/';i++){cout<<name[i];dirs[DirLevInd][index++]=name[i];}cout<<"\t";dirs[DirLevInd][index]='\0';DirLevInd++;}}dirs[DirLevInd]=nullptr;// DirLevInd is cnt of FileLevelreturn true;}else{return false;} }創(chuàng)建目錄及向目錄中添加文件
為了不破壞原本Nachos的參數(shù)系統(tǒng),這里想要通過對添加文件時的文件名進行檢查,通過檢查來確定是否創(chuàng)建目錄。
即:如果新生成/創(chuàng)建的文件的文件名形如./***/findName
那么應該首先檢查當前目錄下是否存在要生成的目錄,如果存在,即進入目錄下。如果不存在,就根據(jù)前者的名字創(chuàng)建目錄,然后再進入目錄下。
根據(jù)當前Nachos文件系統(tǒng)中創(chuàng)建文件的規(guī)則,可以發(fā)現(xiàn)它主要是在../lab5/ftest.cc中的函數(shù)Copy中調(diào)用了fileSystem->Create(to,fileLength)函數(shù)完成了對于文件的創(chuàng)建。
為了檢查是否需要創(chuàng)建目錄,需要在當前的目錄中進行檢查,來檢測目錄是否已經(jīng)存在。
因此這里有一個目錄與文件的標識和區(qū)分問題,即我們檢測目錄名時,目錄名存在的條件為:同名且類型為目錄
根據(jù)實驗四中得知的目錄與文件的相關知識,可以知道目錄的目錄項中包含三個部分:bool inUse,int sector,char* fileName。注意到inUse變量的類型為bool,理論只占用一個bit,但是實際使用中會發(fā)現(xiàn)改位置占用了不止一個字節(jié):
根據(jù)這個特點,如果我在結(jié)構(gòu)中聲明inUse之后創(chuàng)建一個bool類型的變量direct,就可以做到基本不修改表目錄項的內(nèi)容且可以保證目錄表的大小不會有什么變化,維護了原本Nachos 系統(tǒng)的健壯性。
嘗試結(jié)果如下:
其中前者代表inUse,后者代表direct。
數(shù)據(jù)結(jié)構(gòu)確定后,就開始修改函數(shù)。對于創(chuàng)建目錄,所有需要修改的內(nèi)容如下:
-
directory.h
在類DirectoryEntry中添加成員變量bool direct用來標明當前記錄是否為目錄文件。
在類Directory中添加成員函數(shù)
- bool Add(char *name,int newSector,bool isDirect);
對類Directory中成員函數(shù)的修改:
-
使用默認形參的方式不會破壞原本的函數(shù)調(diào)用語法,并且還可以添加新的調(diào)用方式
-
int Find(char *name,bool isDirect=false)
-
int FindIndex(char *name,bool isDirect=false)
-
構(gòu)造函數(shù)的修改(置默認的direct為FALSE)
-
directory.cc
添加的成員函數(shù)Add的實現(xiàn):
bool Directory::Add(char* name,int newSector,bool isDirect) {if(isDirect){// the directory existsif (FindIndex(name,isDirect) != -1)return FALSE;// create a new directoryfor (int i = 0; i < tableSize; i++)if (!table[i].inUse) {table[i].inUse = TRUE;table[i].direct=isDirect;strncpy(table[i].name, name, FileNameMaxLen); table[i].sector = newSector;return TRUE;}return FALSE; // no space. Fix when we have extensible files.}else{printf("Directory::Add : Unexpected Call of Function!\n");Abort();return FALSE;} }修改函數(shù)FindIndex結(jié)果:
修改函數(shù)Find:
int Directory::FindIndex(char *name,bool isDirect) {for (int i = 0; i < tableSize; i++)if (table[i].inUse && !strncmp(table[i].name, name, FileNameMaxLen) && (table[i].direct == isDirect))return i;return -1; // name not in directory }構(gòu)造函數(shù)修改:
-
filesys.h
-
添加函數(shù) bool CreateDir(char** dirs,int fileLength)
其中dirs是存放著每一層目錄名的二維數(shù)組fileLength只代表最后的文件的文件大小。
通過這個函數(shù)逐層創(chuàng)建目錄以及最后創(chuàng)建文件。
-
添加成員函數(shù)Open(char **dirs)
-
添加私有成員函數(shù)Directory* createDir(char* name,Directory* in)
意味著在目錄in中創(chuàng)建一個名稱為name的目錄并返回創(chuàng)建的這個目錄。
-
添加私有成員函數(shù)bool createFile(char* name,Directory* in,int fileLength,OpenFile* in_file)
-
-
filesys.cc
-
CreateDir函數(shù)的實現(xiàn)
循環(huán)對每層目錄進行如下操作:
-
調(diào)用函數(shù)createDir(dirs[i],pre,preFile),代表著在目錄pre中創(chuàng)建名稱為dirs[i]的目錄,對應的文件通過引用傳遞存儲在preFile中。
為了進行傳遞,需要先查找當前目錄是否存在,如果存在就返回當前目錄的指針,但是如果下一級目錄不存在,還要用到當前目錄的存儲位置,因為通過創(chuàng)建目錄以后,當前目錄發(fā)生修改,需要進行寫回操作。
-
檢查是否創(chuàng)建成功。
到最后一層時應當進行文件的創(chuàng)建。
因此直接調(diào)用私有成員函數(shù)createFile(dirs[i],pre,fileLength,preFile)
即向目錄pre中寫入名稱為dirs[i],大小為fileLength的文件,然后將更改后的文件寫回preFile。
最后代碼如下:
bool FileSystem::CreateDir(char** dirs,int fileLength) {int i=0;Directory* pre=new Directory(NumDirEntries);pre->FetchFrom(directoryFile);OpenFile* preFile=directoryFile;for(;dirs[i+1]!=NULL;i++){pre=createDir(dirs[i],pre,preFile);if(pre==NULL){printf("CreateDir: Unable to create directory %s\n",dirs[i]);return false;}}// shoule chreate a filereturn createFile(dirs[i],pre,fileLength,preFile); } -
-
Open的實現(xiàn)(實際上是對原有成員函數(shù)Open(char* name)的重載)
傳入一串目錄和最終的文件名,返回最后的文件名對應文件的OpenFile類指針
OpenFile * FileSystem::Open(char **dirs) { Directory *directory = new Directory(NumDirEntries);OpenFile *openFile = directoryFile;int sector,i;for(i=0;dirs[i+1]!=NULL;i++){DEBUG('f', "Opening directory %s\n", dirs[i]);directory->FetchFrom(openFile);sector=directory->Find(dirs[i],true);// printf("Find Directory %s Header in %d\n",dirs[i],sector);if(sector<0) return NULL;openFile=NULL;openFile=new OpenFile(sector); // get the openfile of next level directory}DEBUG('f', "Opening File %s\n", dirs[i]);directory->FetchFrom(openFile);sector = directory->Find(dirs[i]); // printf("Find File %s Header in %d\n",dirs[i],sector);if (sector < 0) openFile=NULL; else openFile = new OpenFile(sector); // name was found in directory delete directory;return openFile; // return NULL if not found } -
私有函數(shù)createDir 的實現(xiàn)
首先檢查目錄in中是否已經(jīng)存在想要創(chuàng)建的目錄了。
-
如果存在,返回這個目錄。
-
如果不存在,創(chuàng)建這個目錄,返回目錄。
// make directory in the directory `in` // in_file means current dir `in`'s file,when return ,it means the storage file of return Directory Directory* FileSystem::createDir(char* name,Directory* in,OpenFile* &in_file) {BitMap *freeMap;FileHeader *hdr;int sector;bool success;int dirFileH=0;if((dirFileH=in->Find(name,true))!=-1){// the directory exists in current Direcotyin_file=new OpenFile(dirFileH);Directory* ret;ret=new Directory(NumDirEntries);ret->FetchFrom(in_file);return ret;}else{// we should create a fileDEBUG('f',"Creating Directory %s \n",name);Directory* ret;freeMap=new BitMap(NumSectors);freeMap->FetchFrom(freeMapFile);sector=freeMap->Find();if(sector==-1)ret=NULL;else if(!in->Add(name,sector,true))ret=NULL;else{hdr=new FileHeader;if(!hdr->Allocate(freeMap,DirectoryFileSize))ret=NULL;else{success=TRUE;hdr->WriteBack(sector);in->WriteBack(in_file);freeMap->WriteBack(freeMapFile);ret=new Directory(NumDirEntries);in_file=new OpenFile(sector);ret->FetchFrom(in_file);}delete hdr;}delete freeMap;return ret;} }
-
-
私有成員函數(shù)createFile的實現(xiàn)
總體上參考了函數(shù)Create。也就是先檢查文件是否存在,不存在則創(chuàng)建文件,對應修改目錄和位圖等等。
bool FileSystem::createFile(char* name,Directory *in,int fileLength,OpenFile* in_file) {Directory *directory;BitMap *freeMap;FileHeader *hdr;int sector;bool success;DEBUG('f', "Creating file %s, size %d\n", name, fileLength);// make file in the directory `in`directory = in;if (directory->Find(name) != -1)success = FALSE; // file is already in directoryelse {freeMap = new BitMap(NumSectors);freeMap->FetchFrom(freeMapFile);sector = freeMap->Find(); // find a sector to hold the file headerif (sector == -1) success = FALSE; // no free block for file header else if (!directory->Add(name, sector))success = FALSE; // no space in directoryelse {hdr = new FileHeader;if (!hdr->Allocate(freeMap, fileLength))success = FALSE; // no space on disk for dataelse { success = TRUE;// everthing worked, flush all changes back to diskhdr->WriteBack(sector); directory->WriteBack(in_file);freeMap->WriteBack(freeMapFile);}delete hdr;}delete freeMap;}delete directory;return success; }
-
-
fstest.cc
-
更改函數(shù)void Copy(char* from,char* to)
當通過從Linux向Nachos復制文件時,需要檢查Nachos中的文件名是否為目錄下的文件。
因此先調(diào)用ParseFileName函數(shù)。
如果是普通文件,則正常進行,
如果是目錄名,通過調(diào)用的函數(shù)就得到了多級目錄的各自名稱。
然后直接調(diào)用上面提到的CreateDir進行創(chuàng)建:
這里只給出關鍵的部分代碼:
char** dirs; if(ParseFileName(to,dirs)) {if(!fileSystem->CreateDir(dirs,fileLength)){printf("Copy: couldn't create output file %s\n", to);fclose(fp);return;}openFile=fileSystem->Open(dirs); } else //normal files//...
-
刪除目錄及刪除目錄中文件
這里的文件刪除Nachos系統(tǒng)本身具有的刪除非常相似。通過將目錄項中的inUse項設置為False,同時修改位圖,就算刪除了文件。
但是這里還包括了對于目錄的刪除,為了加以區(qū)分,這里通過添加新的Nachos指令rd標識remove directory刪除目錄文件。
-
刪除文件
涉及到的函數(shù)修改主要是filesys.cc中的Remove函數(shù)
要做到刪除目錄下的文件,就需要找到文件所在的最后一層目錄,將最后一層目錄的目錄項中對應文件的inUse設置為FALSE;
程序修改如下:
這里只給出關鍵部分代碼,后續(xù)代碼基本與原Remove函數(shù)一致
char** dirs; if(ParseFileName(name,dirs)) {int i=0;for(i=0;dirs[i+1]!=NULL;i++){sector=directory->Find(dirs[i],true);if(sector==-1){delete dirFile;delete directory;return FALSE;}dirFile=new OpenFile(sector);directory->FetchFrom(dirFile);}// current directory is the last level of directory// and dirs[i] is the name of the filename=dirs[i]; } -
刪除目錄
首先要明確一點,即刪除目錄時,目錄下的文件也要隨之刪除。
因此可以采用遞歸的方式。刪除當前文件前,先刪除掉當前文件下的所有文件和目錄,再刪除當前目錄。
首先進行查找,找到要刪除目錄的上一級目錄。然后調(diào)用函數(shù)dir->Clear()將該目錄下的所有文件和目錄清空,然后參考Remove函數(shù)將目錄dir從當前目錄中刪除。
對于命令-rd的處理,采用如下方法:
-
函數(shù)RemoveDir的實現(xiàn):
void FileSystem::RemoveDir(char* name) {// printf("Entry\n");char **dirs;OpenFile *dirFile=directoryFile;Directory *dir=new Directory(NumDirEntries);dir->FetchFrom(dirFile);int i=0;int sector=0;if(!ParseFileName(name,dirs)){printf("You Should give a directory but not a file name\n");return ;}for(i=0;dirs[i+1]!=NULL;i++){sector=dir->Find(dirs[i]);if(sector<0){printf("Cann't Find Directory %s\n",dirs[i]);return;}dirFile=new OpenFile(sector);dir->FetchFrom(dirFile);}// printf("Ready to CLear!\n");sector=dir->Find(dirs[i],true);if(sector<0){printf("RemoveDir: Unable to Find the directory %s\n",dirs[i]);}OpenFile* delFile=new OpenFile(sector);Directory* delDir=new Directory(NumDirEntries);delDir->FetchFrom(delFile);// clear the content of directory dirs[i]delDir->Clear(freeMapFile,delFile,NumDirEntries);FileHeader* fileHdr = new FileHeader;fileHdr->FetchFrom(sector);BitMap* freeMap = new BitMap(NumSectors);freeMap->FetchFrom(freeMapFile);fileHdr->Deallocate(freeMap); // remove data blocksfreeMap->Clear(sector); // remove header blockif(!dir->Remove(dirs[i],true))printf("RemoveDir: Unable to Remove directory %s\n",dirs[i]);freeMap->WriteBack(freeMapFile); // flush to diskdir->WriteBack(dirFile); // flush to diskdelete dir;delete fileHdr;delete freeMap;delete delDir;delete delFile;delete dirFile; } -
函數(shù)Clear的實現(xiàn):
bool Directory::Clear(OpenFile* freeMapFile,OpenFile* curFile,int n) {FileHeader* fileHdr=new FileHeader;BitMap* freeMap=new BitMap(NumSectors);freeMap->FetchFrom(freeMapFile);OpenFile* delFile;Directory* delDir=new Directory(n);for(int i=0;i<n;i++){if(table[i].inUse){if(table[i].sector<0) return FALSE;if(table[i].direct){//delete a directorydelFile=new OpenFile(table[i].sector);delDir->FetchFrom(delFile);delDir->Clear(freeMapFile,delFile,n);}fileHdr->FetchFrom(table[i].sector);fileHdr->Deallocate(freeMap);freeMap->Clear(table[i].sector);Remove(table[i].name);}}freeMap->WriteBack(freeMapFile);this->WriteBack(curFile);delete fileHdr;delete freeMap;return true; }
-
展示目錄以及目錄中的文件內(nèi)容
這里以Nachos系統(tǒng)中的指令作為區(qū)分。
-
首先是對于Nachos相關參數(shù)中的**-p參數(shù)。**
調(diào)用了../fstest.cc中的Print(char *name)函數(shù),輸出名稱為name的文件的內(nèi)容。
通過閱讀函數(shù)內(nèi)部可以知道,這里是直接調(diào)用了FileSystem::open(char* name)用來獲取文件的文件句柄。
為了滿足多層級目錄的設置,在這里調(diào)用通用函數(shù)ParseFileName進行判斷。
- 如果只是單個文件,則直接輸出。
- 如果在子目錄下,通過多級目錄的名稱找到文件后再輸出。
實現(xiàn)如下:
void Print(char *name) {OpenFile *openFile; int i, amountRead;char *buffer;char** dirs;if(ParseFileName(name,dirs)){if((openFile=fileSystem->Open(dirs))==NULL){printf("Print: unable to open file %s\n", name);return;}}else{if ((openFile = fileSystem->Open(name)) == NULL) {printf("Print: unable to open file %s\n", name);return;}}buffer = new char[TransferSize];while ((amountRead = openFile->Read(buffer, TransferSize)) > 0)for (i = 0; i < amountRead; i++)printf("%c", buffer[i]);delete [] buffer;delete openFile; // close the Nachos filereturn; } -
對于參數(shù)-l,這里對參數(shù)設置進行添加,即設置參數(shù)-ld,意為list directory。
在處理參數(shù)的界面添加以下代碼:
else if(!strcmp(*argv,"-ld")) {ASSERT(argc>1);fileSystem->ListDir(*(argv+1)); }-
對../lab5/filesys.h添加函數(shù):
void ListDir(char* name)
-
對../lab5/filesys.cc
函數(shù)ListDir的實現(xiàn)
功能為傳入目錄名,然后找到最后一個目錄名對應的目錄,輸出目錄下的所有文件名和目錄名。首先解析文件名,得到一連串的目錄名。然后逐層尋找目錄中的下一級目錄文件。
void FileSystem::ListDir(char* name) {char** dirs;if(ParseFileName(name,dirs)){OpenFile* curFile=directoryFile;Directory* dir=new Directory(NumDirEntries);dir->FetchFrom(curFile);int sector;for(int i=0;dirs[i]!=NULL;i++){if((sector=dir->Find(dirs[i],true))<0){printf("ListDir: Unable to find directory:%s\n",dirs[i]);return;}curFile=new OpenFile(sector);dir->FetchFrom(curFile);}dir->List();delete curFile;delete dir;return;}else{delete dirs;printf("Unable to List file \n");} } -
為了增加區(qū)分度,對List時調(diào)用的函數(shù)../lab5/directory.cc->Directory::List()進行修改:
輸出目錄時顏色設置為藍色。
為了將目錄下的子目錄內(nèi)容和文件都進行輸出,這里對List函數(shù)進行改進,更改為遞歸輸出。
為了輸出時可以比較明顯地看出層次結(jié)構(gòu),這里通過更改縮進地方式來完成對于縮進的改進。為了表現(xiàn)這一點,就需要確認產(chǎn)生的縮進層次,這一點通過更改List函數(shù)的傳入?yún)?shù)確定。即令其傳入?yún)?shù)為int類型數(shù)字n,代表輸出文件名前要輸出的\t的個數(shù)。
為了不改變原有的函數(shù)功能,默認n=0
void Directory::List(int cntTable) {// printf the table space to show the levelchar TableSpace[cntTable+1];for(int i=0;i<cntTable;i++)TableSpace[i]='\t';TableSpace[cntTable]='\0';for (int i = 0; i < tableSize; i++){if (table[i].inUse){if(table[i].direct){// DirectoryDirectory* dir=new Directory(10);OpenFile* dirFile=new OpenFile(table[i].sector);// printf("FetchBefore!\n");dir->FetchFrom(dirFile);printf("%s\033[34m\033[1m%s\033[0m\n",TableSpace,table[i].name);// printf("ListBefore!\n"); dir->List(cntTable+1);delete dir;delete dirFile;}else{// normal Filesprintf("%s%s\n",TableSpace,table[i].name);}}} }
-
-
對于參數(shù)-D,注意到對于參數(shù)-D,產(chǎn)生調(diào)用時會調(diào)用根目錄的函數(shù)Print
為了在輸出時更好地區(qū)分調(diào)用內(nèi)容,這里采用對輸出內(nèi)容進行著色的方式進行輸出。
函數(shù)更改如下(只貼關鍵的代碼):
for (int i = 0; i < tableSize; i++) {if (table[i].inUse && !table[i].direct) {printf("\033[0mName: %s, Sector: %d\n", table[i].name, table[i].sector);hdr->FetchFrom(table[i].sector);hdr->Print();}else if(table[i].inUse && table[i].direct) {printf("\033[34m\033[1mDirectory: %s, Sector: %d\n",table[i].name,table[i].sector);hdr->FetchFrom(table[i].sector);hdr->Print();printf("\033[0m");} }
多級目錄的測試
-
首先make。
-
然后通過./nachos -f生成DISK磁盤文件
-
通過./nachos -cp ./test/small ./dir/ttt/small將Linux文件small復制到Nachos下的目錄./dir/ttt下。
-
通過./nachos -ld ./dir/ttt查看ttt目錄下的內(nèi)容
通過./nachos -p ./dir/ttt/small嘗試輸出文件內(nèi)容:
-
通過./nachos -r ./dir/ttt/small嘗試刪除文件,可以看到此時的ttt目錄下已經(jīng)沒有文件了
再通過./nachos -D進一步查看內(nèi)容,與刪除前相比,位圖中的第11,12塊被釋放,說明文件small已經(jīng)成功刪除
-
通過./nachos -rd ./dir刪除目錄及其子目錄,可以發(fā)現(xiàn),目錄文件的inUse項被設置為了0。同時位圖也釋放了。刪除成功!
-
類文件樹的輸出測試:
重新創(chuàng)建一個DISK進行后續(xù)測試。
通過幾個命令,使得整體目錄結(jié)構(gòu)為:
- 根目錄下包含文件small,目錄dir
- 目錄dir下包含文件file,目錄ttt和ttt2
- 目錄ttt下包含一個文件file
- 目錄ttt2下包含一個文件file2
命令如下:
./nachos -cp ./test/small small ./nachos -cp ./test/empty ./dir/file ./nachos -cp ./test/small ./dir/ttt/file ./nachos -cp ./test/empty ./dir/ttt2/file2然后通過以下命令分別進行輸出查看:
./nachos -l ./nachos -ld ./dir
感受
整個實驗相對復雜的地方在于對于文件系統(tǒng)的擴展。為了實現(xiàn)文件系統(tǒng)的擴展,需要對文件的創(chuàng)造和刪除非常熟悉,還需要對目錄文件和整個文件系統(tǒng)存儲的結(jié)構(gòu)有比較深得了解。擴展文件系統(tǒng)的過程中對諸多函數(shù)都產(chǎn)生了更改,并且加入了新的Nachos參數(shù)和相關函數(shù)。函數(shù)之間的相互調(diào)用錯綜復雜,對于函數(shù)運行中可能出現(xiàn)的錯誤都要盡量考慮周到,例如創(chuàng)建OpenFile類對象的時候,一定要先檢查構(gòu)造函數(shù)需要的參數(shù)sector的正負情況!編寫新的函數(shù)時一定要仔細考慮好將該函數(shù)添加到哪個類中,作為類的成員函數(shù)。函數(shù)添加到這個類中還有沒有復用性更高的方案?能否將這個函數(shù)獨立出來以供使用?一系列問題都是在構(gòu)建大型項目時需要仔細考慮的。
雖然非常麻煩,但是程序成功運行時的快樂也是無與倫比的!
總結(jié)
以上是生活随笔為你收集整理的Nachos操作系统-文件系统添加多级目录的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python用MySQLdb, pyms
- 下一篇: Android recycleview使