現(xiàn)代文件系統(tǒng)(例如Ext4和XFS等)具有多種附加特性蛔翅,不僅擴(kuò)展了文件系統(tǒng)的應(yīng)用場(chǎng)景泡嘴,而且使得文件系統(tǒng)的容錯(cuò)性(例如日志特性)和性能得到很大的提高选脊。而下一代文件系統(tǒng)(例如Btrfs和ZFS)則提供了更加高級(jí)的功能特性,比如存儲(chǔ)池、RAID支持和快照等特性矮慕,使得文件系統(tǒng)超出了嚴(yán)格文件系統(tǒng)的界限,甚至具備的卷管理的能力啄骇。
文件系統(tǒng)已經(jīng)發(fā)展的如此完善痴鳄,我們是否有必要在去了解那些老古董。本號(hào)以為是有這個(gè)比較要的缸夹。一方面是通過這個(gè)我們可以了解文件系統(tǒng)最初的樣子痪寻,理解文件系統(tǒng)的理念;另外一方面是現(xiàn)代的問題系統(tǒng)都比較龐大(最少的也幾萬行代碼)虽惭,不容易理解橡类,而老的文件系統(tǒng)才幾千行代碼,理解起來比較容易芽唇。
廢話說了半天顾画,還沒進(jìn)入正題。首先我們可能想知道Ext4的老祖宗到底是誰匆笤,我們知道Linux操作系統(tǒng)是參考MINIX操作系統(tǒng)寫的亲雪。而Linux操作系統(tǒng)的第一代文件系統(tǒng)Ext也正式參考該系統(tǒng)的實(shí)現(xiàn)。目前在最新的內(nèi)核代碼樹仍然保留這Minix文件系統(tǒng)疚膊。該文件系統(tǒng)功能特性非常簡(jiǎn)單义辕,能力有限,但麻雀雖小五臟俱全寓盗,代碼總量才2千行左右灌砖。
圖1 Minix文件系統(tǒng)代碼統(tǒng)計(jì)
Linux系統(tǒng)中文件系統(tǒng)的基本原理是比較簡(jiǎn)單的,簡(jiǎn)單的理解就是2次映射的過程傀蚌。一次是根據(jù)文件名找到inode節(jié)點(diǎn)基显,第二次是根據(jù)inode節(jié)點(diǎn)找到文件存儲(chǔ)數(shù)據(jù)的位置。
Minix的數(shù)據(jù)布局
在學(xué)習(xí)任何一個(gè)本地文件系統(tǒng)之前善炫,我們最好先對(duì)其數(shù)據(jù)的布局有一個(gè)整體的認(rèn)識(shí)撩幽。這樣,我們?cè)诶斫鈹?shù)據(jù)讀寫邏輯和元數(shù)據(jù)等代碼時(shí)才能更容易一些箩艺。這個(gè)是符合人類認(rèn)識(shí)事物的規(guī)律的窜醉,因?yàn)槿祟愓J(rèn)識(shí)事物總是從具體到抽象,從簡(jiǎn)單到復(fù)雜的艺谆。對(duì)于文件系統(tǒng)磁盤布局是比較具體的內(nèi)容榨惰,代碼實(shí)現(xiàn)則是比較抽象的內(nèi)容,因此從磁盤的數(shù)據(jù)布局開始比較容易一些静汤。
minix文件系統(tǒng)的布局比較簡(jiǎn)單琅催,如圖2是該文件系統(tǒng)的磁盤布局居凶。包含的主要內(nèi)容為:?jiǎn)?dòng)塊、超級(jí)塊藤抡、inode位圖侠碧、zone位圖、inode列表和存儲(chǔ)數(shù)據(jù)的zone缠黍。需要注意的是minix存儲(chǔ)的數(shù)據(jù)是以zone為單位的(默認(rèn)為1KB)弄兜,而不是以磁盤的扇區(qū)。
我們?cè)贚inux系統(tǒng)下進(jìn)行格式化的時(shí)候嫁佳,可以看到輸出該文件系統(tǒng)的基本信息,其內(nèi)容如下:
# mkfs.minix /dev/loop0
160 inodes
400 blocks
Firstdatazone=9 (9)
Zonesize=1024
Maxsize=268966912
超級(jí)塊
超級(jí)塊是整個(gè)文件系統(tǒng)的入口谷暮,里面包含inode數(shù)量蒿往、數(shù)據(jù)塊數(shù)量、zone大小和第一個(gè)zone的位置等湿弦。如下是代碼中對(duì)超級(jí)塊的定義(本圖是V1版本瓤漏,minix有多個(gè)版本,本文以V1版本為例):
inode位圖和zone位圖
這兩個(gè)位圖分別占用一個(gè)塊的數(shù)據(jù)颊埃,通過其中的一個(gè)位(bit)來表示對(duì)應(yīng)的inode或者zone是否已經(jīng)被使用了蔬充。如果已經(jīng)被使用了則為1,否則為0班利。如圖為位圖在磁盤上的數(shù)據(jù)示例饥漫。
inode列表
在文件系統(tǒng)中通過inode記錄文件的元數(shù)據(jù)(管理數(shù)據(jù))信息。在minix文件系統(tǒng)中inode記錄著文件的模式信息罗标、時(shí)間信息和文件數(shù)據(jù)位置信息等內(nèi)容庸队。如圖是minix V1版本inode的結(jié)構(gòu),該結(jié)構(gòu)占用32字節(jié)闯割。inode表可以理解為一個(gè)inode的數(shù)組彻消,inode在表中依次排列,通過偏移就可以找到指定的inode信息宙拉。
在V2版本中對(duì)inode進(jìn)行了調(diào)整宾尚,inode具備3級(jí)間接塊和更加豐富的時(shí)間信息。如圖是minix V2版本的inode節(jié)點(diǎn)結(jié)構(gòu)谢澈。
目錄與文件
在Linux操作系統(tǒng)中煌贴,任何文件系統(tǒng)都有一個(gè)根目錄,minix也不例外锥忿。在Linux中崔步,目錄是一種特殊的文件,其中的數(shù)據(jù)存儲(chǔ)的是一個(gè)名為目錄項(xiàng)的數(shù)據(jù)缎谷。根據(jù)目錄項(xiàng)井濒,我們可以實(shí)現(xiàn)從文件名到文件元數(shù)據(jù)的查找灶似。在minix中目錄項(xiàng)的結(jié)構(gòu)體如下所示。
struct minix_dir_entry {
__u16 inode;
char name[0];
};
目錄項(xiàng)的內(nèi)容很簡(jiǎn)單瑞你,其中兩個(gè)域分別是inode的id和文件名酪惭。因此,在目錄中檢索是者甲,通過文件名可以找到inode的id春感,而該id其實(shí)就是inode表中inode的索引。
文件創(chuàng)建
理解了上面關(guān)于磁盤布局和目錄內(nèi)容的相關(guān)內(nèi)容虏缸,對(duì)后續(xù)的內(nèi)容也就容易理解了鲫懒。從上面的介紹,我們可以看出文件的創(chuàng)建主要涉及如下幾個(gè)方面:
- 從inode列表選擇一個(gè)沒有被使用inode刽辙,并標(biāo)記對(duì)應(yīng)的位圖
- 在父目錄存儲(chǔ)數(shù)據(jù)的位置添加一個(gè)目錄項(xiàng)
- 為該inode分配需要存儲(chǔ)數(shù)據(jù)的zone窥岩,并標(biāo)記對(duì)應(yīng)的位圖
上面是從用戶角度來說的,其實(shí)對(duì)于第3步從內(nèi)核角度應(yīng)該屬于寫數(shù)據(jù)的流程了宰缤,而非創(chuàng)建文件的流程颂翼。
在代碼層面,創(chuàng)建文件的函數(shù)調(diào)用流程為minix_create->minix_mknod->minix_new_inode慨灭,這個(gè)流程是從inode表中分配inode的核心流程朦乏。另外一個(gè)流程是minix_create->minix_mknod->add_nondir,這個(gè)流程是在父目錄中創(chuàng)建目錄項(xiàng)的流程氧骤。具體代碼實(shí)現(xiàn)都比較簡(jiǎn)單呻疹,我們這里就不貼代碼了。
數(shù)據(jù)讀寫
minix文件系統(tǒng)并沒有定義自己的數(shù)據(jù)讀寫接口筹陵,如圖所示诲宇,這些接口都是VFS框架提供的通用讀寫接口。調(diào)用這里面的寫接口惶翻,數(shù)據(jù)將被寫入頁緩存中姑蓝,而后續(xù)在調(diào)用底層的接口實(shí)現(xiàn)向磁盤寫數(shù)據(jù)的流程。
圖3 文件訪問接口
最終調(diào)用底層的函數(shù)集合來完成實(shí)際的數(shù)據(jù)讀寫吕粗。具體的流程可以參考本號(hào)之前關(guān)于Ext2和Ext4等文件系統(tǒng)中關(guān)于讀寫的介紹纺荧,本文不再贅述。
分配磁盤塊
對(duì)于寫數(shù)據(jù)來說颅筋,核心的內(nèi)容就是分配磁盤塊宙暇,分配磁盤塊其實(shí)就是上文中所說的第二個(gè)映射關(guān)系。也就是议泵,文件的inode與文件數(shù)據(jù)之間的映射關(guān)系占贫。
我們先了解一下minix文件系統(tǒng)是如何組織文件數(shù)據(jù)的。前文我們看到了在inode里面有一個(gè)i_zone的數(shù)組先口,其大小為9型奥。這個(gè)數(shù)組就是用來存儲(chǔ)文件數(shù)據(jù)的位置信息的瞳收。在這個(gè)數(shù)組中,其前7個(gè)元素直接存儲(chǔ)文件數(shù)據(jù)的位置信息厢汹。而第8個(gè)元素存儲(chǔ)的不是文件的數(shù)據(jù)位置信息螟深,但也是一個(gè)磁盤塊,而在該磁盤塊中存儲(chǔ)的文件的位置信息烫葬。以此類推界弧,第9個(gè)元素通過2級(jí)塊來記錄文件的數(shù)據(jù)位置信息。這種中間有1級(jí)或者2級(jí)磁盤塊存儲(chǔ)文件位置信息的方式稱為間接塊的方式搭综。如下是minix文件系統(tǒng)文件存儲(chǔ)數(shù)據(jù)的示意圖垢箕。
為了更加容易理解,我們舉幾個(gè)例子(minix文件系統(tǒng)的塊大小為1KB)兑巾。假設(shè)文件比較小条获,只有幾十個(gè)字節(jié),此時(shí)通過一個(gè)塊就可以存儲(chǔ)這些數(shù)據(jù)闪朱,因此通過i_zone中的第一個(gè)元素就可以表示該文件的數(shù)據(jù)月匣。如上圖中钻洒,第一個(gè)元素為50奋姿,表示數(shù)據(jù)存儲(chǔ)在磁盤偏移為50個(gè)塊的位置。
如果文件的數(shù)據(jù)大于7KB素标,比如最簡(jiǎn)單的位于8KB的位置称诗。此時(shí)前7個(gè)元素只能存儲(chǔ)7KB的數(shù)據(jù),因此需要用到第8個(gè)元素头遭。而第8個(gè)元素存儲(chǔ)的是一個(gè)間接塊的位置信息寓免,比如上圖中52是間接塊的位置。而在該間接塊中會(huì)依次存儲(chǔ)文件邏輯偏移對(duì)應(yīng)的數(shù)據(jù)的位置计维。比如該間接塊中第一個(gè)數(shù)據(jù)為57袜香,表示文件8KB偏移的數(shù)據(jù)存儲(chǔ)在磁盤57KB的位置。
由于minix文件系統(tǒng)是通過間接塊的方式存儲(chǔ)文件中的數(shù)據(jù)的鲫惶。因此這里核心的邏輯是根據(jù)用戶寫數(shù)據(jù)的位置和大小計(jì)算出來需要的磁盤塊的數(shù)量蜈首,同時(shí)界定存儲(chǔ)的位置信息應(yīng)該在inode中i_zone數(shù)組的什么位置。這個(gè)邏輯是通過get_block函數(shù)實(shí)現(xiàn)的欠母,大家可以自行看一下欢策,本文不再贅述。
分析磁盤布局
結(jié)合我們前面介紹的超級(jí)塊赏淌、磁盤布局和文件數(shù)據(jù)組織的內(nèi)容踩寇。我們可以實(shí)際分析一下磁盤上的數(shù)據(jù)。比如我們以前文格式化的磁盤為例六水,可以通過dd工具將數(shù)據(jù)導(dǎo)出到一個(gè)文件中俺孙。并通過vim工具以二進(jìn)制的方式打開辣卒。
超級(jí)塊
前文說了,超級(jí)塊是入口鼠冕,我們先看一下超級(jí)塊添寺。在格式化完成后,工具給出了如下信息:
160 inodes
400 blocks
Firstdatazone=9 (9)
Zonesize=1024
Maxsize=268966912
這些信息其實(shí)就是超級(jí)塊的主要信息懈费。根據(jù)磁盤的布局计露,我們知道超級(jí)塊位于第2個(gè)塊(塊大小為1KB)位置,由于磁盤以0位開始憎乙,因此其起始位置為1KB票罐,終止位置為2KB(0x400正是這個(gè)位置)。
結(jié)合前面超級(jí)塊的定義泞边,s_ninodes表示的是inode的數(shù)量该押,16位小端對(duì)齊。因此下圖中的a000表示的是該字段的內(nèi)容阵谚,轉(zhuǎn)換為16進(jìn)制為0x00a0蚕礼,也就是10進(jìn)制的160,這個(gè)數(shù)據(jù)正好是上面格式化的輸出信息中的inode數(shù)量信息梢什。其它數(shù)據(jù)也可以對(duì)應(yīng)奠蹬,請(qǐng)自行分析。
inode位圖
接下來是inode位圖嗡午,這部分?jǐn)?shù)據(jù)從第3個(gè)塊開始囤躁,也就是2KB的位置(0x800)。下圖的數(shù)據(jù)是位圖的數(shù)據(jù)荔睹,可以看出標(biāo)1的為已經(jīng)使用的inode狸演,0位沒有使用的inode。
數(shù)據(jù)位圖
以此類推僻他,數(shù)據(jù)塊位圖位于第4個(gè)塊的位置宵距,不過多解釋了。
inode表
inode表位于第5個(gè)塊的位置吨拗,每個(gè)inode占用32字節(jié)满哪。其中第15個(gè)字節(jié)為i_zones的起始位置,可以對(duì)照上文結(jié)構(gòu)體看一下丢胚。本文創(chuàng)建了3個(gè)非常小的文件翩瓜,名稱分別是a、b和c携龟。對(duì)比如下導(dǎo)出的數(shù)據(jù)兔跌,其中0x1000-0x101F的數(shù)據(jù)是跟目錄的inode。其中0x09表示其數(shù)據(jù)的位置在磁盤第9個(gè)塊(9KB)的位置峡蟋。
目錄數(shù)據(jù)內(nèi)容
我們導(dǎo)出這里的數(shù)據(jù)(0x2400)坟桅,在結(jié)合目錄項(xiàng)的定義我們可以對(duì)其中的內(nèi)容進(jìn)行分析华望。
struct minix_dir_entry {
__u16 inode;
char name[0];
};
可以看出每一項(xiàng)占用32字節(jié),其中前2項(xiàng)分別是當(dāng)前目錄和上一級(jí)目錄(也就是. 和 ..),后面是具體文件的信息仅乓±抵郏可以看出后面3項(xiàng)分別是0x0002,61(字符a)、0x0003,62(字符b)和0x0004,63(字符c)夸楣。
文件數(shù)據(jù)內(nèi)容
以文件a為例宾抓,根據(jù)前面的信息可以知道其inode id為2,因此在從inode表中找到其i_zones的內(nèi)容為0x0a豫喧,也就是數(shù)據(jù)位于10KB的位置(0x2800)石洗。如圖所示,可以看出這里的內(nèi)容為616161...紧显,也就是我們寫入的aaa...字符讲衫。
到這里我們對(duì)Ext4文件系統(tǒng)的老祖宗minix文件系統(tǒng)有了一個(gè)基本的了解,也對(duì)如何分析文件系統(tǒng)的數(shù)據(jù)有了概要的認(rèn)識(shí)孵班。今天先到這涉兽,后續(xù)我們?cè)賹?duì)文件系統(tǒng)相關(guān)的內(nèi)容進(jìn)行更加深入的介紹。