linux 檔案系統的實現

2021-07-04 10:00:11 字數 3763 閱讀 1599

linux檔案管理從使用者的層面介紹了linux管理檔案的方式。linux有乙個樹狀結構來組織檔案。樹的頂端為根目錄(/),節點為目錄,而末端的葉子為包含資料的檔案。當我們給出乙個檔案的完整路徑時,我們從根目錄出發,經過沿途各個目錄,最終到達檔案。

我們可以對檔案進行許多操作,比如開啟和讀寫。在linux檔案管理相關命令中,我們看到許多對檔案進行操作的命令。它們大都基於對檔案的開啟和讀寫操作。比如cat可以開啟檔案,讀取資料,最後在終端顯示:

$cat test.txt
對於linux下的程式設計師來說,了解檔案系統的底層組織方式,是深入進行系統程式設計所必備的。即使是普通的linux使用者,也可以根據相關的內容,設計出更好的系統維護方案。

檔案系統的最終目的是把大量資料有組織的放入永續性(persistant)的儲存裝置中,比如硬碟和磁碟。這些儲存裝置與記憶體不同。它們的儲存能力具有永續性,不會因為斷電而消失;儲存量大,但讀取速度慢。

觀察常見儲存裝置。最開始的區域是mbr,用於linux開機啟動(參考linux開機啟動)。剩餘的空間可能分成數個分割槽(partition)。每個分割槽有乙個相關的分割槽表(partition table),記錄分割槽的相關資訊。這個分割槽表是儲存在分割槽之外的。分割槽表說明了對應分割槽的起始位置和分割槽的大小。

我們在windows系統常常看到c分割槽、d分割槽等。linux系統下也可以有多個分割槽,但都被掛載在同乙個檔案系統樹上。

資料被存入到某個分割槽中。乙個典型的linux分割槽(partition)包含有下面各個部分:

分割槽的第乙個部分是啟動區(boot block),它主要是為計算機開機服務的。linux開機啟動後,會首先載入mbr,隨後mbr從某個硬碟的啟動區引導程式。該程式負責進一步的作業系統的載入和啟動。為了方便管理,即使某個分割槽中沒有安裝作業系統,linux也會在該分割槽預留啟動區。

啟動區之後的是超級區(super block)。它儲存有檔案系統的相關資訊,包括檔案系統的型別,inode的數目,資料塊的數目。

隨後是多個inodes,它們是實現檔案儲存的關鍵。在linux系統中,乙個檔案可以分成幾個資料塊儲存,就好像是分散在各地的龍珠一樣。為了順利的收集齊龍珠,我們需要乙個「雷達」的指引:該檔案對應的inode。每個檔案對應乙個inode。這個inode中包含多個指標,指向屬於該檔案各個資料塊。當作業系統需要讀取檔案時,只需要對應inode的"地圖",收集起分散的資料塊,就可以收穫我們的檔案了。

最後一部分,就是真正儲存資料的資料塊們(data blocks)了。

上面我們看到了儲存裝置的巨集觀結構。我們要深入到分割槽的結構,特別是檔案在分割槽中的儲存方式。

檔案是檔案系統對資料的分割單元。檔案系統用目錄來組織檔案,賦予檔案以上下分級的結構。在硬碟上實現這一分級結構的關鍵,是使用inode來虛擬普通檔案和目錄檔案物件。

在linux檔案管理中,我們知道,乙個檔案除了自身的資料之外,還有乙個附屬資訊,即檔案的元資料(metadata)。這個元資料用於記錄檔案的許多資訊,比如檔案大小,擁有人,所屬的組,修改日期等等。元資料並不包含在檔案的資料中,而是由作業系統維護的。事實上,這個所謂的元資料就包含在inode中。我們可以用$ls -l filename來檢視這些元資料。正如我們上面看到的,inode所佔據的區域與資料塊的區域不同。每個inode有乙個唯一的整數編號(inode number)表示。

在儲存元資料,inode是「檔案」從抽象到具體的關鍵。正如上一節中提到的,inode儲存由一些指標,這些指標指向儲存裝置中的一些資料塊,檔案的內容就儲存在這些資料塊中。當linux想要開啟乙個檔案時,只需要找到檔案對應的inode,然後沿著指標,將所有的資料塊收集起來,就可以在記憶體中組成乙個檔案的資料了。

資料塊在1, 32, 0, ...

inode並不是組織檔案的唯一方式。最簡單的組織檔案的方法,是把檔案依次順序的放入儲存裝置,***就採取了類似的方式。但如果有刪除操作,刪除造成的空餘空間夾雜在正常檔案之間,很難利用和管理。

複雜的方式可以使用鍊錶,每個資料塊都有乙個指標,指向屬於同一檔案的下乙個資料塊。這樣的好處是可以利用零散的空餘空間,壞處是對檔案的操作必須按照線性方式進行。如果想隨機訪問,那麼必須遍歷鍊錶,直到目標位置。由於這一遍歷不是在記憶體進行,所以速度很慢。

fat系統是將上面鍊錶的指標取出,放入到記憶體的乙個陣列中。這樣,fat可以根據記憶體的索引,迅速的找到乙個檔案。這樣做的主要問題是,索引陣列的大小與資料塊的總數相同。因此,儲存裝置很大的話,這個索引陣列會比較大。

inode既可以充分利用空間,在記憶體佔據空間不與儲存裝置相關,解決了上面的問題。但inode也有自己的問題。每個inode能夠儲存的資料塊指標總數是固定的。如果乙個檔案需要的資料塊超過這一總數,inode需要額外的空間來儲存多出來的指標。

在linux中,我們通過解析路徑,根據沿途的目錄檔案來找到某個檔案。目錄中的條目除了所包含的檔名,還有對應的inode編號。當我們輸入$cat /var/test.txt時,linux將在根目錄檔案中找到var這個目錄檔案的inode編號,然後根據inode合成var的資料。隨後,根據var中的記錄,找到text.txt的inode編號,沿著inode中的指標,收集資料塊,合成text.txt的資料。整個過程中,我們參考了三個inode:根目錄檔案,var目錄檔案,text.txt檔案的inodes。

在linux下,可以使用$stat filename,來查詢某個檔案對應的inode編號。

在儲存裝置中實際上儲存為:

當我們讀取乙個檔案時,實際上是在目錄中找到了這個檔案的inode編號,然後根據inode的指標,把資料塊組合起來,放入記憶體供進一步的處理。當我們寫入乙個檔案時,是分配乙個空白inode給該檔案,將其inode編號記入該檔案所屬的目錄,然後選取空白的資料塊,讓inode的指標指像這些資料塊,並放入記憶體中的資料。

在linux的程序中,當我們開啟乙個檔案時,返回的是乙個檔案描述符。這個檔案描述符是乙個陣列的下標,對應陣列元素為乙個指標。有趣的是,這個指標並沒有直接指向檔案的inode,而是指向了乙個檔案**,再通過該**,指向載入到記憶體中的目標檔案的inode。如下圖,乙個程序開啟了兩個檔案。

可以看到,每個檔案**中記錄了檔案開啟的狀態(status flags),比如唯讀,寫入等,還記錄了每個檔案的當前讀寫位置(offset)。當有兩個程序開啟同乙個檔案時,可以有兩個檔案**,每個檔案**對應的開啟狀態和當前位置不同,從而支援一些檔案共享的操作,比如同時讀取。

要注意的是程序fork之後的情況,子程序將只複製檔案描述符的陣列,而和父程序共享核心維護的檔案**和inode。此時要特別小心程式的編寫。

這裡概括性的總結了linux的檔案系統。linux以inode的方式,讓資料形成檔案。

了解linux的檔案系統,是深入了解操作系linux原理的重要一步。

歡迎閱讀linux的概念與體系系列文章

Linux檔案系統實現

linux檔案管理從使用者的層面介紹了linux管理檔案的方式。linux有乙個樹狀結構來組織檔案。樹的頂端為根目錄 節點為目錄,而末端的葉子為包含資料的檔案。當我們給出乙個檔案的完整路徑時,我們從根目錄出發,經過沿途各個目錄,最終到達檔案。我們可以對檔案進行許多操作,比如開啟和讀寫。在linux檔...

Linux檔案系統的實現

linux檔案管理從使用者的層面介紹了linux管理檔案的方式。linux有乙個樹狀結構來組織檔案。樹的頂端為根目錄 節點為目錄,而末端的葉子為包含資料的檔案。當我們給出乙個檔案的完整路徑時,我們從根目錄出發,經過沿途各個目錄,最終到達檔案。我們可以對檔案進行許多操作,比如開啟和讀寫。在linux檔...

Linux檔案系統的實現

linux檔案管理從使用者的層面介紹了linux管理檔案的方式。linux有乙個樹狀結構來組織檔案。樹的頂端為根目錄 節點為目錄,而末端的葉子為包含資料的檔案。當我們給出乙個檔案的完整路徑時,我們從根目錄出發,經過沿途各個目錄,最終到達檔案。我們可以對檔案進行許多操作,比如開啟和讀寫。在linux檔...