操作系統的一個課程設計,實現一個二級文件夾文件系統。
用disk.txt模擬磁盤,使用Help查看支持的命令及其操作方式,root為超級用戶(寫在disk.txt中) 文件的邏輯結構:流式文件。 物理結構:鏈接文件。
物理空間管理:空暇鏈法。
文件夾結構:二級文件夾結構。 文件夾搜索技術:線性搜索。
FCB:含文件相關的所有屬性。
物理盤塊的設計(disk.txt)
以一個文本文件disk.txt模擬硬盤,設定硬盤容量分為100個物理塊,每一個物理塊的大小512字節(為了測試方便,最后68個數據塊每一個的大小為256字節),盤塊之間用(‘\n’)切割。
因此一個盤塊:512字節數據+1字節(‘\n’)切割符=513字節。則disk.txt 長度=51300(100×513)+1字節(文件結束符)=51301字節。
100塊盤塊的分布:
1#: MFD塊,存放MFD信息;
2-17#: UFD塊,存放UFD信息;
18-33#: UOF塊,存放UOF信息;
其余物理塊用於存放文件內容。
# MFD塊的設計
硬盤的第1個物理塊固定用於存放主文件文件夾MFD。MFD結構例如以下:
typedef struct mfd{ username ;//username 14B userpwd ;//password14B link; //該用戶的UFD所在的物理塊號(4B) }MFD;
每一個MFD項占32字節。因此,1個物理塊可存放512/32=16個MFD(用戶),即本文件系統最多可管理16個用戶。例如以下表1所看到的:
username |
password |
用戶文件文件夾地址 |
Peter |
12345 |
3 |
Ben |
Abc |
5 |
表1 文件系統用戶文件夾信息表
2#-17# UFD塊的設計
2#-17#物理塊:固定用於存放用戶文件文件夾UFD。
假設一個用戶須要一個UFD塊。因此,16個用戶共須要16個UFD塊。
UFD結構例如以下:
typedef struct { filename //文件名稱14B; mode; ///文件權限0-readonly;1-writeonly;2-read/write length; ///文件長度(以字節數計算) addr;//該文件的第1個文件塊對應的物理塊號 }UFD;
一個UFD項設為32 Bytes。一個塊可存放16個UFD項。則一個用戶最多可創建16個文件。例如以下表2所看到的:
Filename |
Mode |
Length |
Addr |
A |
1 |
3 |
50 |
B |
2 |
5 |
52 |
表2 用戶文件文件夾信息表
18#-33# UOF塊的設計
18#-33#物理塊:固定用於存放主文件文件夾UOF,假定一個用戶須要一個塊存放UOF。一個UOF項占32字節,則一個塊可存放512/32=16個UOF,即一個用戶可同一時候打開的文件數為16個。用戶已打開表”(UOF)。用以說明用戶當前正在使用文件的情況。
假設用戶最多同一時候找開或建立16個文件。則用戶已打開文件表UOF應該有16個登記欄,結構例如以下表3所看到的:
文件名稱 |
文件屬性 |
狀態(打開/建立) |
讀指針 |
寫指針 |
應該為16個登記欄 |
表3 主文件文件夾信息表
用戶請求打開或建立一個文件時。對應的文件操作把有關該文件的信息登記到UOF中。讀指針和寫打針用於指出對文件進行存取的對應位置。
34#-100# 數據塊的設計
34#-100#:數據塊(物理塊每一個256字節),用於存放文件內容;為了實現物理塊的分配和回收,程序始終維護一個空暇物理塊表。以物理塊號從小到大排列。
物理塊以鏈接分配方式,以最先適應法從空暇表中分配。
數據結構例如以下:
typedef struct cluster {Num ;////物理塊號 long nextcluster;/////指向下一物理塊號 }Cluster;
主要結構分析清楚了之后,程序流程圖就不在這里畫了。
我使用的二維vector存儲這些結構體,每次程序啟動的時候先從文件里讀取這些信息至內存,各種信息直接在內存中改動。使用sysc指令將內存中的信息同步至disk.txt。
須要注意的幾個問題:(一)函數指針的運用
在眾多對輸入命令的函數處理中,假設採用if..else..或者switch..case..的方法勢必會造成代碼的冗余以及代碼簡潔度的缺失。在這里我用到了函數指針的方法,定義一個結構體專門用於存儲命令的字符串和對應的函數處理名稱(函數指針),這樣僅僅須要一次for循環遍歷就能夠簡潔高效的處理這些命令,既體現了代碼規范中的簡潔高效的規則,又幫助自己深刻理解了C++語法中函數指針的應用。
typedef void(*func)(void); typedef struct hand { char *pname; func handler; }HAND_TO;
HAND_TO handlerlist[] = { { "Chmod", do_Chmod }, { "Chown", do_Chown }, { "Mv", do_Mv }, { "Copy", do_Copy }, { "Type", do_Type }, { "Passwd", do_Passwd }, { "Login", do_Login }, { "Logout", do_Logout }, { "Create", do_Create }, { "Delete", do_Delete }, { "Open", do_Open }, { "Close", do_Close }, { "Write", do_Write }, { "Read", do_Read }, { "Help", do_Help }, { "dir", do_dir}, { "sysc",do_sysc}, { "Register", do_register}, { "Exit", do_exit}, { "Clear", do_Clear}, { NULL, NULL } };
在對文件內容的分塊存儲中。由於UOF中僅僅記錄了文件內容的起始物理塊。這對於寫指針來說定位當前位置是能夠實現的,由於我僅僅須要記錄最后一個物理塊的偏移量就可以。追加寫入的時候直接迭代到最后一個物理塊進行寫入。
可是對於讀指針來說。僅僅記錄最后一個物理塊的偏移量是不能夠的,由於我上一次讀的位置不一定位於文件的末尾,這樣就會產生沒有數據記錄當前讀的物理塊的塊號。對此我的解決方法是讀指針記錄當前字節的總數,這樣讀指針/256能夠得出當前的物理塊的塊號。讀指針%256能夠得出當前讀的物理塊的偏移量。
問題得到了完美的解決。
void do_Write() { //Write filename buffer nbytes 寫文件 物理空間68 int is_ok = 0; for (int i = 0; i < FileState[curID].size(); i++) { if (strcmp(FileState[curID][i].filename, cmd_in.cmd_num[1].c_str()) == 0) { is_ok = 1; break; } } if (is_ok == 0) { cout << "文件尚未打開!" << endl; return; } char buf[1024]; stringstream ss; ss << cmd_in.cmd_num[3]; int temp; ss >> temp; for (int i = 0; i < FileInfo[curID].size(); i++) { if (strcmp(FileInfo[curID][i].filename, cmd_in.cmd_num[1].c_str()) == 0) { if (FileInfo[curID][i].mode == 1 || FileInfo[curID][i].mode == 2)//推斷權限 { break; } else { cout << "沒有寫的權限!" << endl; return; } } } int index; for (int i = 0; i < FileState[curID].size(); i++) { if (strcmp(FileState[curID][i].filename, cmd_in.cmd_num[1].c_str()) == 0) { index = i; break; } } //起始物理塊 int address; for (int i = 0; i < FileInfo[curID].size(); i++) { if (strcmp(FileInfo[curID][i].filename, cmd_in.cmd_num[1].c_str()) == 0) { address = FileInfo[curID][i].addr; break; } } //注意:此處發生了更改。 cout << "請輸入buff的內容:" << endl; gets(buf); fflush(stdin); //strcpy(buf, cmd_in.cmd_num[2].c_str()); int wbegin; wbegin = FileState[curID][index].write_poit; //找到寫指針所在的最后一個磁盤 while (FileCluster[address].next_num != address) address = FileCluster[address].next_num; vector <int> newspace_num;//計算將要占用的物理塊的數量 newspace_num.clear(); //int num = (256-wbegin+temp) / 256-1; if (temp <= 256 - wbegin) num = 0; else { num = ceil((temp - (256 - wbegin))*1.0 / 256); } newspace_num.push_back(address); //cout << newspace_num.size() << endl;// for (int i = 0; i < FileCluster.size(); i++) { if (newspace_num.size() == num+1) break; if (FileCluster[i].is_data == 0) { newspace_num.push_back(i); FileCluster[i].is_data = 1; } } for (int k = 0; k < newspace_num.size() - 1; k++) { FileCluster[newspace_num[k]].next_num = newspace_num[k + 1]; } for (int i = 0; i < temp; i++) { if (wbegin == 256) { wbegin = 0; address = FileCluster[address].next_num; } FileCluster[address].data[wbegin] = buf[i]; wbegin++; } //更新寫指針 FileState[curID][index].write_poit = wbegin; cout << "磁盤寫入成功!" << endl; return; }
本實驗所有源代碼請到我的 Github下載,也歡迎大家fork繼續進行完好。