思考題
Thinking 5.1
查閱資料,了解 Linux/Unix 的 /proc 文件系統是什么?有什么作用? Windows 操作系統又是如何實現這些功能的?proc 文件系統這樣的設計有什么好處和可以改進的地方?
/proc文件系統是一種特殊的,由軟件創建的(偽)文件系統,內核使用它向外界導出信息,/proc系統只存在內存當中,而不占用外存空間。/proc下面的每個文件都綁定於一個內核函數,用戶讀取文件時,該函數動態地生成文件的內容。
與其它常見的文件系統不同的是,/proc是一種偽文件系統(也即虛擬文件系 統),存儲的是當前內核運行狀態的一系列特殊文件,用戶可以通過這些文件查看有關系統硬件及當前正在運行進程的信息,甚至可以通過更改其中某些文件來改變 內核的運行狀態。
Windows,分盤,每個驅動器有自己的根目錄,形成的是多個樹並列的結構。
Linux,只有一個根目錄 / ,所有東西都是從這開始
Thinking 5.2
如果我們通過 kseg0 讀寫設備,我們對於設備的寫入會緩存到 Cache 中。通過 kseg0 訪問設備是一種錯誤的行為,在實際編寫代碼的時候這么做會引發不可預知的問題。請你思考:這么做這會引起什么問題?對於不同種類的設備(如 我們提到的串口設備和 IDE 磁盤)的操作會有差異嗎?可以從緩存的性質和緩存刷新的策略來考慮。
因為內核被放在kseg0區域,一般通過cache訪問;如果對設備的寫入緩存到cache中,就會導致以后想訪問內核時卻錯誤訪問了寫入設備的內容。
Thinking 5.3
一個磁盤塊最多存儲 1024 個指向其他磁盤塊的指針,試計算,我們 的文件系統支持的單個文件的最大大小為多大?
4KB*1024=4MB
Thinking 5.4
查找代碼中的相關定義,試回答一個磁盤塊中最多能存儲多少個文件控制塊?一個目錄下最多能有多少個文件?
1個磁盤塊中最多能存儲16個文件控制塊,一個目錄下最多能有1024*16=16384個文件
Thinking 5.5
請思考,在滿足磁盤塊緩存的設計的前提下,我們實驗使用的內核支持的最大磁盤大小是多少?
0x6f3fd000
Thinking 5.6
如果將 DISKMAX 改成 0xC0000000, 超過用戶空間,我們的文件系統還能正常工作嗎?為什么?
不能,因為緩存磁盤塊的時候可能會把內核的內容覆蓋掉,導致系統運行異常
Thinking 5.7
閱讀 user/file.c ,你會發現很多函數中都會將一個 struct Fd * 型的 指針轉換為 struct Filefd * 型的指針,請解釋為什么這樣的轉換可行。
因為在結構體Filefd中儲存的第一個元素就是struct Fd*,因而對於相匹配的一對struct Fd和struct Filefd,他們的指針實際上指向了相同的虛擬地址,所以可以通過指針轉化訪問struct Filefd中的其他元素
Thinking 5.8
請解釋 Fd, Filefd, Open 結構體及其各個域的作用。比如各個結構體 會在哪些過程中被使用,是否對應磁盤上的物理實體還是單純的內存數據等。說明 形式自定,要求簡潔明了,可大致勾勒出文件系統數據結構與物理實體的對應關系 與設計框架。
Fd結構體用於表示文件描述符,fd_dev_id表示文件所在設備的id,fd_offset表示讀或者寫文件的時候,距離文件開頭的偏移量,fd_omode用於描述文件打開的讀寫模式。它主要用於在打開文件之后記錄文件的狀態,以便對文件進行管理/讀寫,不對應物理實體,只是單純的內存數據。
Filefd結構體是文件描述符 和 文件的組合形式,f_fd記錄了文件描述符,f_fileid記錄了文件的id,f_file則記錄了文件控制塊,包含文件的信息以及指向儲存文件的磁盤塊的指針,對應了磁盤的物理實體,也包含內存數據。
Open結構體在文件系統進程用用於儲存文件相關信息,o_file指向了對應的文件控制塊,o_fileid表示文件id用於在數組opentab中查找對應的Open,o_mode記錄文件打開的狀態,o_ff指向對應的Filefd結構體。
Thinking 5.9 閱讀serv.c/serve函數的代碼,我們注意到函數中包含了一個死循
環for (;😉 {...},為什么這段代碼不會導致整個內核進入panic 狀態?
因為再調用ipc_recv的時候這個進程會等待其他進程向他發送請求文件系統調用再繼續執行,如果沒有接收到請求就會在這里一直等待,因此不會自己執行死循環
難點
Exercise5.1
請根據lib/syscall_all.c中的說明,完成sys_write_dev函數和sys_read_dev 函數,並且在 user/lib.h,user/syscall_lib.c 中完成用戶態的相應系統調用的接口。
編寫這兩個系統調用時需要注意物理地址、用戶進程虛擬地址同內核虛擬地址 之間的轉換。 同時還要檢查物理地址的有效性,在實驗中允許訪問的地址范圍為: console: [0x10000000, 0x10000020), disk: [0x13000000, 0x13004200), rtc: [0x15000000, 0x15000200)
,當出現越界時,應返回指定的錯誤碼。
重點注意要覆蓋所有非法情況,但也不要把合法的邊界誤判了!合法之后直接bcopy即可
(exp:dst+len = 0x10000020是合法的 並沒有使用0x10000020的地址寫東西)
Exercise 5.2
參考內核態驅動,完成 fs/ide.c 中的 ide_write 函數,以及 ide_read 函數,實現對磁盤的讀寫操作
注意理解這里需要我們進行哪些操作!每0x200大小的讀寫操作都需要把所有流程執行一遍!
讀取磁盤的時候:
寫入磁盤號,寫入磁盤讀取位置偏移,寫入0表示開始讀取,讀出操作結果(是否讀取成功),從緩沖區讀取數據
寫磁盤的時候:
寫入磁盤號,寫入磁盤寫入位置偏移,數據寫入緩沖區,寫入1表示開始寫,讀出操作結果
Exercise 5.3
文件系統需要負責維護磁盤塊的申請和釋放,在回收一個磁盤塊時,需 要更改位圖中的標志位。如果要將一個磁盤塊設置為 free,只需要將位圖中對應的 位的值設置為 1 即可。請完成 fs/fs.c 中的 free_block 函數,實現這一功能。同時 思考為什么參數 blockno 的值不能為 0 ?
每32個磁盤的標志位存在bitmap上的一個32位數上,所以根據blockno除以32求出對應數組的第幾位,再對32取模得到是32位上的第幾位並且置1即可
Exercise 5.4
請參照文件系統的設計,完成 fsformat.c 中的 create_file函數,並按 照個人興趣完成 write_directory 函數(不作為考察點),實現將一個文件或指定 目錄下的文件按照目錄結構寫入到 fs/fs.img 的根目錄下的功能。關於如何創建二 進制文件的鏡像,請參考 fs/Makefile。 在實現的過程中,你可以將你的實現同我們給出的參考可執行文件tools/fsformat 進行對比。具體來講,你可以通過 Linux 提供的 xxd 命令將兩個 fsformat 產生的二 進制鏡像轉化為可閱讀的文本文件,手工進行查看或使用 diff 等工具進行對比。
分類討論多種情況
如果還沒有一個block被使用,創建一個新的
如果已經有了,那么遍歷尋找一個空的文件控制塊位置(注意直接和間接訪問的區別)
如果遍歷之后沒有找到,創建一個新的
Exercise 5.5
fs/fs.c 中的 diskaddr 函數用來計算指定磁盤塊對應的虛存地址。完成 diskaddr 函數,根據一個塊的序號 (block number),計算這一磁盤塊對應的 512 bytes 虛存的起始地址。(提示:fs/fs.h 中的宏 DISKMAP 和 DISKMAX 定義了磁盤映射虛存的地址空間)。
直接用DISKMAP + BY2BLK * blockno即可
Exercise 5.6
實現 map_block 函數,檢查指定的磁盤塊是否已經映射到內存,如果沒有,分配一頁內存來保存磁盤上的數據。對應地,完成 unmap_block 函數,用於解除磁盤塊和物理內存之間的映射關系,回收內存。(提示:注意磁盤虛擬內存地址 空間和磁盤塊之間的對應關系)。
map中:用block_is_mapped(blockno)檢查是否已經映射到內存並得到虛擬內存va,如果沒有則調用syscall_mem_alloc分配相應空間
unmap中:用block_is_mapped(blockno)檢查是否已經映射到內存並得到虛擬內存va,如果塊不是free的並且被修改了,write_block(blockno),寫回。調用syscall_mem_unmap(0,va);解除映射關系
Exercise 5.7
補全 dir_lookup 函數,查找某個目錄下是否存在指定的文件。(提示: 使用file_get_block可以將某個指定文件指向的磁盤塊讀入內存)。
先計算目錄下有多少個塊,然后依次遍歷用file_get_block(dir,i,&blk)得到的每個塊,查看里面的文件控制塊並對比文件名字是否是要查找的,如果是返回0
Exercise 5.8
完成 user/file.c 中的 open 函數。(提示:若成功打開文件則該函數返 回文件描述符的編號)。
分配一個新的文件描述符Fd,然后根據path用fsipc_open打開對應文件。從中讀取文件相關的信息,之后分配內存加載文件的內容,最后返回文件描述符的標號
Exercise 5.9
參考 user/fd.c 中的 write 函數,完成 read 函數
完全仿照write即可,通過fd_lookup和dev_lookup查找對應fd和dev,然后檢查mode是否合格,根據debug決定是否輸出,從seek標記的位置開始讀取文件,之后修改文件中的標記位置。最后給讀出的字符串加上結束的\0
Exercise5.10
文件user/fsipc.c中定義了請求文件系統時用到的IPC操作,user/file.c 文件中定義了用戶程序讀寫、創建、刪除和修改文件的接口。完成 user/fsipc.c 中的 fsipc_remove函數、user/file.c中的remove函數,以及fs/serv.c中的serve_remove 函數,實現刪除指定路徑的文件的功能。*
fsipc_remove先檢查path是否合格,然后創建Fsreq_remove結構體並傳遞path進去,最后用fsipc發送請求
serve_remove復制出來path之后調用file_remove,把對應的結果用ipc發送回去
remove函數直接調用fsipc_remove
體會與感想
相比起lab4的地獄難度,lab5的填寫工作確實要簡單一些,不過這並不代表着lab5就更容易理解了。只是很多相關調用都是已經寫好的,想要看懂整個機制還是需要閱讀大量的代碼進行自己的思考。雖然lab5課下測試拿了滿分,代碼也研究了很長時間,但是還是不敢說就把文件系統的整個過程弄明白了。
感覺助教寫的補充指導書對於幫助理解還是起了至關重要的作用。僅僅依靠大指導書的內容,對於該填寫什么,填寫的東西是干什么用的完全一頭霧水。感謝助教付出的辛苦勞動。