操作系統的一個課程設計,實現一個二級目錄文件系統。
用disk.txt模擬磁盤,使用Help查看支持的命令及其操作方式,root為超級用戶(寫在disk.txt中)
文件的邏輯結構:流式文件。
物理結構:鏈接文件。
物理空間管理:空閒鏈法。
目錄結構:二級目錄結構。
目錄搜索技術:線性搜索。
FCB:含文件相關的全部屬性。
以一個文本文件disk.txt模擬硬盤,設定硬盤容量分為100個物理塊,每個物理塊的大小512字節,盤塊之間用(‘\n’)分割。因此一個盤塊:512字節數據+1字節(‘\n’)分割符=513字節,則disk.txt 長度=51300(100×513)+1字節(文件結束符)=51301字節。
100塊盤塊的分布:
1#: MFD塊,存放MFD信息;
2-17#: UFD塊,存放UFD信息;
18-33#: UOF塊,存放UOF信息;
其余物理塊用於存放文件內容。
硬盤的第1個物理塊固定用於存放主文件目錄MFD。MFD結構如下:
typedef struct mfd{ username ;//用戶名 14B userpwd ;//密碼14B link; //該用戶的UFD所在的物理塊號(4B) }MFD;
每個MFD項占32字節,因此,1個物理塊可存放512/32=16個MFD(用戶),即本文件系統最多可管理16個用戶。如下表1所示:
用戶名
密碼
用戶文件目錄地址
Peter
12345
3
Ben
Abc
5
表1 文件系統用戶目錄信息表
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,假定一個用戶需要一個塊存放UOF,一個UOF項占32字節,則一個塊可存放512/32=16個UOF,即一個用戶可同時打開的文件數為16個。用戶已打開表”(UOF),用以說明用戶當前正在使用文件的情況。如果用戶最多同時找開或建立16個文件,則用戶已打開文件表UOF應該有16個登記欄,結構如下表3所示:
文件名
文件屬性
狀態(打開/建立)
讀指針
寫指針
應該為16個登記欄
表3 主文件目錄信息表
用戶請求打開或建立一個文件時,相應的文件操作把有關該文件的信息登記到UOF中,讀指針和寫打針用於指出對文件進行存取的相應位置。
34#-100#:數據塊(物理塊),用於存放文件內容;為了實現物理塊的分配和回收,程序始終維護一個空閒物理塊表,以物理塊號從小到大排列。物理塊以鏈接分配方式,以最先適應法從空閒表中分配。數據結構如下:
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; vectornewspace_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; }