操作系統的一個課程設計,實現一個二級文件夾文件系統。
用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繼續進行完好。
