現代文件系統(例如Ext4和XFS等)具有多種附加特性,不僅擴展了文件系統的應用場景,而且使得文件系統的容錯性(例如日誌特性)和性能得到很大的提高。而下一代文件系統(例如Btrfs和ZFS)則提供了更加高級的功能特性,比如存儲池、RAID支持和快照等特性,使得文件系統超出了嚴格文件系統的界限,甚至具備的卷管理的能力。

文件系統已經發展的如此完善,我們是否有必要在去了解那些老古董。本號以為是有這個比較要的。一方面是通過這個我們可以了解文件系統最初的樣子,理解文件系統的理念;另外一方面是現代的問題系統都比較龐大(最少的也幾萬行代碼),不容易理解,而老的文件系統才幾千行代碼,理解起來比較容易。

廢話說了半天,還沒進入正題。首先我們可能想知道Ext4的老祖宗到底是誰,我們知道Linux操作系統是參考MINIX操作系統寫的。而Linux操作系統的第一代文件系統Ext也正式參考該系統的實現。目前在最新的內核代碼樹仍然保留這Minix文件系統。該文件系統功能特性非常簡單,能力有限,但麻雀雖小五臟俱全,代碼總量才2千行左右。

圖1 Minix文件系統代碼統計

Linux系統中文件系統的基本原理是比較簡單的,簡單的理解就是2次映射的過程。一次是根據文件名找到inode節點,第二次是根據inode節點找到文件存儲數據的位置。

Minix的數據布局

在學習任何一個本地文件系統之前,我們最好先對其數據的布局有一個整體的認識。這樣,我們在理解數據讀寫邏輯和元數據等代碼時才能更容易一些。這個是符合人類認識事物的規律的,因為人類認識事物總是從具體到抽象,從簡單到複雜的。對於文件系統磁碟布局是比較具體的內容,代碼實現則是比較抽象的內容,因此從磁碟的數據布局開始比較容易一些。

minix文件系統的布局比較簡單,如圖2是該文件系統的磁碟布局。包含的主要內容為:啟動塊、超級塊、inode點陣圖、zone點陣圖、inode列表和存儲數據的zone。需要注意的是minix存儲的數據是以zone為單位的(默認為1KB),而不是以磁碟的扇區。

我們在Linux系統下進行格式化的時候,可以看到輸出該文件系統的基本信息,其內容如下:

# mkfs.minix /dev/loop0
160 inodes
400 blocks
Firstdatazone=9 (9)
Zonesize=1024
Maxsize=268966912

超級塊

超級塊是整個文件系統的入口,裡面包含inode數量、數據塊數量、zone大小和第一個zone的位置等。如下是代碼中對超級塊的定義(本圖是V1版本,minix有多個版本,本文以V1版本為例):

inode點陣圖和zone點陣圖

這兩個點陣圖分別佔用一個塊的數據,通過其中的一個位(bit)來表示對應的inode或者zone是否已經被使用了。如果已經被使用了則為1,否則為0。如圖為點陣圖在磁碟上的數據示例。

inode列表

在文件系統中通過inode記錄文件的元數據(管理數據)信息。在minix文件系統中inode記錄著文件的模式信息、時間信息和文件數據位置信息等內容。如圖是minix V1版本inode的結構,該結構佔用32位元組。inode表可以理解為一個inode的數組,inode在表中依次排列,通過偏移就可以找到指定的inode信息。

在V2版本中對inode進行了調整,inode具備3級間接塊和更加豐富的時間信息。如圖是minix V2版本的inode節點結構。

目錄與文件

在Linux操作系統中,任何文件系統都有一個根目錄,minix也不例外。在Linux中,目錄是一種特殊的文件,其中的數據存儲的是一個名為目錄項的數據。根據目錄項,我們可以實現從文件名到文件元數據的查找。在minix中目錄項的結構體如下所示。

struct minix_dir_entry {
__u16 inode;
char name[0];
};

目錄項的內容很簡單,其中兩個域分別是inode的id和文件名。因此,在目錄中檢索是,通過文件名可以找到inode的id,而該id其實就是inode表中inode的索引

文件創建

理解了上面關於磁碟布局和目錄內容的相關內容,對後續的內容也就容易理解了。從上面的介紹,我們可以看出文件的創建主要涉及如下幾個方面:

  1. 從inode列表選擇一個沒有被使用inode,並標記對應的點陣圖
  2. 在父目錄存儲數據的位置添加一個目錄項
  3. 為該inode分配需要存儲數據的zone,並標記對應的點陣圖

上面是從用戶角度來說的,其實對於第3步從內核角度應該屬於寫數據的流程了,而非創建文件的流程。

在代碼層面,創建文件的函數調用流程為minix_create->minix_mknod->minix_new_inode,這個流程是從inode表中分配inode的核心流程。另外一個流程是minix_create->minix_mknod->add_nondir,這個流程是在父目錄中創建目錄項的流程。具體代碼實現都比較簡單,我們這裡就不貼代碼了。

數據讀寫

minix文件系統並沒有定義自己的數據讀寫介面,如圖所示,這些介面都是VFS框架提供的通用讀寫介面。調用這裡面的寫介面,數據將被寫入頁緩存中,而後續在調用底層的介面實現向磁碟寫數據的流程。

圖3 文件訪問介面

最終調用底層的函數集合來完成實際的數據讀寫。具體的流程可以參考本號之前關於Ext2和Ext4等文件系統中關於讀寫的介紹,本文不再贅述。

分配磁碟塊

對於寫數據來說,核心的內容就是分配磁碟塊,分配磁碟塊其實就是上文中所說的第二個映射關係。也就是,文件的inode與文件數據之間的映射關係。

我們先了解一下minix文件系統是如何組織文件數據的。前文我們看到了在inode裡面有一個i_zone的數組,其大小為9。這個數組就是用來存儲文件數據的位置信息的。在這個數組中,其前7個元素直接存儲文件數據的位置信息。而第8個元素存儲的不是文件的數據位置信息,但也是一個磁碟塊,而在該磁碟塊中存儲的文件的位置信息。以此類推,第9個元素通過2級塊來記錄文件的數據位置信息。這種中間有1級或者2級磁碟塊存儲文件位置信息的方式稱為間接塊的方式。如下是minix文件系統文件存儲數據的示意圖。

為了更加容易理解,我們舉幾個例子(minix文件系統的塊大小為1KB)。假設文件比較小,只有幾十個位元組,此時通過一個塊就可以存儲這些數據,因此通過i_zone中的第一個元素就可以表示該文件的數據。如上圖中,第一個元素為50,表示數據存儲在磁碟偏移為50個塊的位置。

如果文件的數據大於7KB,比如最簡單的位於8KB的位置。此時前7個元素只能存儲7KB的數據,因此需要用到第8個元素。而第8個元素存儲的是一個間接塊的位置信息,比如上圖中52是間接塊的位置。而在該間接塊中會依次存儲文件邏輯偏移對應的數據的位置。比如該間接塊中第一個數據為57,表示文件8KB偏移的數據存儲在磁碟57KB的位置。

由於minix文件系統是通過間接塊的方式存儲文件中的數據的。因此這裡核心的邏輯是根據用戶寫數據的位置和大小計算出來需要的磁碟塊的數量,同時界定存儲的位置信息應該在inode中i_zone數組的什麼位置。這個邏輯是通過get_block函數實現的,大家可以自行看一下,本文不再贅述。

分析磁碟布局

結合我們前面介紹的超級塊、磁碟布局和文件數據組織的內容。我們可以實際分析一下磁碟上的數據。比如我們以前文格式化的磁碟為例,可以通過dd工具將數據導出到一個文件中。並通過vim工具以二進位的方式打開。

超級塊

前文說了,超級塊是入口,我們先看一下超級塊。在格式化完成後,工具給出了如下信息:

160 inodes
400 blocks
Firstdatazone=9 (9)
Zonesize=1024
Maxsize=268966912

這些信息其實就是超級塊的主要信息。根據磁碟的布局,我們知道超級塊位於第2個塊(塊大小為1KB)位置,由於磁碟以0位開始,因此其起始位置為1KB,終止位置為2KB(0x400正是這個位置)。

結合前面超級塊的定義,s_ninodes表示的是inode的數量,16位小端對齊。因此下圖中的a000表示的是該欄位的內容,轉換為16進位為0x00a0,也就是10進位的160,這個數據正好是上面格式化的輸出信息中的inode數量信息。其它數據也可以對應,請自行分析。

inode點陣圖

接下來是inode點陣圖,這部分數據從第3個塊開始,也就是2KB的位置(0x800)。下圖的數據是點陣圖的數據,可以看出標1的為已經使用的inode,0位沒有使用的inode。

數據點陣圖

以此類推,數據塊點陣圖位於第4個塊的位置,不過多解釋了。

inode表

inode表位於第5個塊的位置,每個inode佔用32位元組。其中第15個位元組為i_zones的起始位置,可以對照上文結構體看一下。本文創建了3個非常小的文件,名稱分別是a、b和c。對比如下導出的數據,其中0x1000-0x101F的數據是跟目錄的inode。其中0x09表示其數據的位置在磁碟第9個塊(9KB)的位置。

目錄數據內容

我們導出這裡的數據(0x2400),在結合目錄項的定義我們可以對其中的內容進行分析。

struct minix_dir_entry {
__u16 inode;
char name[0];
};

可以看出每一項佔用32位元組,其中前2項分別是當前目錄和上一級目錄(也就是. 和 ..),後面是具體文件的信息。可以看出後面3項分別是0x0002,61(字元a)、0x0003,62(字元b)和0x0004,63(字元c)。

文件數據內容

以文件a為例,根據前面的信息可以知道其inode id為2,因此在從inode表中找到其i_zones的內容為0x0a,也就是數據位於10KB的位置(0x2800)。如圖所示,可以看出這裡的內容為616161...,也就是我們寫入的aaa...字元。

到這裡我們對Ext4文件系統的老祖宗minix文件系統有了一個基本的了解,也對如何分析文件系統的數據有了概要的認識。今天先到這,後續我們再對文件系統相關的內容進行更加深入的介紹。

推薦閱讀:

相关文章