程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> 關於C >> 操作系統: 二級目錄文件系統的實現(c/c++語言)

操作系統: 二級目錄文件系統的實現(c/c++語言)

編輯:關於C

操作系統的一個課程設計,實現一個二級目錄文件系統。

用disk.txt模擬磁盤,使用Help查看支持的命令及其操作方式,root為超級用戶(寫在disk.txt中)

文件的邏輯結構:流式文件。
物理結構:鏈接文件。
物理空間管理:空閒鏈法。
目錄結構:二級目錄結構。
目錄搜索技術:線性搜索。
FCB:含文件相關的全部屬性。

物理盤塊的設計(disk.txt)

以一個文本文件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信息;

其余物理塊用於存放文件內容。

# MFD塊的設計

硬盤的第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塊的設計

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#:數據塊(物理塊),用於存放文件內容;為了實現物理塊的分配和回收,程序始終維護一個空閒物理塊表,以物理塊號從小到大排列。物理塊以鏈接分配方式,以最先適應法從空閒表中分配。數據結構如下:

 

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  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繼續進行完善。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved