本文描述Ext4文件系統磁盤佈局和元數據的一些分析,一樣適用於Ext3和Ext2文件系統,除了它們不支持的Ext4的特性外。整個分析分兩篇博文,分別概述佈局和詳細介紹各個佈局的數據結構及組織尋址方式等。感興趣的看官敬請留意和指導!node
1. Ext4文件系統佈局綜述
一個Ext4文件系統被分紅一系列塊組。爲減小磁盤碎片產生的性能瓶頸,塊分配器儘可能保持每一個文件的數據塊都在同一個塊組中,從而減小尋道時間。以4KB的數據塊爲例,一個塊組能夠包含32768個數據塊,也就是128MB。linux
1.1 磁盤佈局
Ext4文件系統的標準磁盤佈局以下:數組
Ext4文件系統主要使用塊組0中的超級塊和塊組描述符表,在其餘一些特定塊組中有超級塊和塊組描述符表的冗餘備份。若是塊組中不含冗餘備份,那麼塊組就以數據塊位圖開始。當格式化磁盤成爲Ext4文件系統的時候,mkfs將在塊組描述符表後面分配預留GDT表數據塊(「Reserve GDT blocks」)以用於未來擴展文件系統。緊接在預留GDT表數據塊後的是數據塊位圖與inode表位圖,這兩個位圖分別表示本塊組內的數據塊與inode表的使用,inode表數據塊以後就是存儲文件的數據塊了。在這些各類各樣的塊中,超級塊、GDT、塊位圖、Inode位圖都是整個文件系統的元數據,固然inode表也是文件系統的元數據,可是inode表是與文件一一對應的,我更傾向於將inode當作文件的元數據,由於在實際格式化文件系統的時候,除了已經使用的十來個外,其餘inode表中其實是沒有任何數據的,直到建立了相應的文件纔會分配inode表,文件系統纔會在inode表中寫入與文件相關的inode信息。緩存
1.2 Flexible 塊組(flex_bg)
Flexible 塊組(flex_bg)是從Ext4開始引入的新特性。在一個flex_bg中,幾個塊組在一塊兒組成一個邏輯塊組flex_bg。Flex_bg的第一個塊組中的位圖空間和inode表空間擴大爲包含了flex_bg中其餘塊組上位圖和inode表。安全
好比flex_bg包含4個塊組,塊組0將按序包含超級塊、塊組描述符表、塊組0-3的數據塊位圖、塊組0-3的inode位圖、塊組0-3的inode表,塊組0中的其餘空間用於存儲文件數據。同時,其餘塊組上的數據塊位圖、inode位圖、inode表元數據就不存在了,可是SB和GDT仍是存在的。
Flexible塊組的做用是:數據結構
(1) 彙集元數據,加速元數據載入;佈局
(2) 使得大文件在磁盤上儘可能連續;post
即便開啓flex_bg特性,超級塊和塊組描述符的冗餘備份仍然位於塊組的開頭。 Flex_bg中塊組的個數由2^ext4_super_block.s_log_groups_per_flex 給出。性能
1.3 元塊組(Meta Block Groups)
一般,在每一個冗餘備份的超級塊的後面是一個完整的(包含全部塊組描述符的)塊組描述符表的備份。這樣會產生一個限制,以Ext4的塊組描述符大小64 Bytes計算,文件系統中最多隻能有2^21個塊組,也就是文件系統最大爲256TB。大數據
使用元塊組Meta Block Groups特性,每一個塊組都包含該塊組本身的描述符的冗餘備份。所以能夠建立2^33個塊組,也就是文件系統最大1EB。48位數據塊,每一個塊組128MB,於是能夠建立2^33個塊組。
元塊組其實是能夠用一個塊組描述符塊來進行描述的塊組集,簡單的說,它由一系列塊組組成,同時這些塊組對應的塊組描述符存儲在一個塊中。它的出現使得Ext3和Ext4的磁盤佈局有了必定的變化,以往超級塊後緊跟的是變長的GDT塊,如今是超級塊依然決定因而否是3,5,7的冪,而一個塊組描述符塊則存儲在元塊組的第一個,第二個和最後一個塊組的開始處(見下圖)
在兩種狀況下咱們可能會用到這種新佈局:
(1) 文件系統建立時。用戶能夠指定使用這種佈局。
(2) 當文件系統增加並且預留的組描述符塊耗盡時。目前超級塊中有一個域s_first_meta_bg用於描述第一個使用元塊組的塊組。
當增長新塊組時,咱們不須要給組描述符表預留空間,而是在當前文件系統後面直接添加新的元塊組就能夠了。
1.4 Lazy 塊組初始化
若是塊組中的相應標誌已設置,那麼塊組中的inode位圖和inode表將不被初始化。這樣能夠減小mkfs時間,若是開啓了塊組描述符校驗和功能,甚至連塊組均可以不初始化。
1.5 特殊inodes
Ext4預留了一些inode作特殊特性使用,見下表:
表 1 Ext4的特殊inode
Inode號 用途
0 不存在0號inode
1 損壞數據塊鏈表
2 根目錄
3 ACL索引
4 ACL數據
5 Boot loader
6 未刪除的目錄
7 預留的塊組描述符inode
8 日誌inode
11 第一個非預留的inode,一般是lost+found目錄
1.6 數據塊和Inode分配策略
在機械磁盤上,保持相關的數據塊相互接近能夠總的磁頭移動時間,於是能夠加速磁盤IO。在SSD上雖然沒有磁頭轉動,數據局部性能夠增長每次IO請求的傳輸的數據大小,於是減小響應IO請求的傳輸次數。數據的局部性對單個擦除塊的寫入產生影響,能夠加速文件重寫的速度。於是儘量減小碎片是必要的。inode和數據塊的分配策略能夠保證數據的局部集中。如下爲inode和數據塊的分配策略:
(1) 多塊分配能夠減小磁盤碎片。當文件初次建立的時候,塊分配器預測性地分配8KB的磁盤空間給文件。當文件關閉的時候,未使用的空間固然也就釋放了。可是若是推測是正確的,那麼文件數據將寫到一個多個塊的extent中。
(2) 延遲分配。當一個文件須要更多的數據塊引發寫操做時,文件系統推遲決定新數據在磁盤上的存放位置,直到髒的buffer寫到磁盤爲止。
(3) 儘可能保持文件的數據塊與其inode在同一個塊組中。能夠減小磁盤尋道時間.
(4) 儘可能保持同一個目錄中的全部inodes與目錄位於同一個塊組中。這樣的假設前提是一個目錄中的文件是相關的。
(5) 磁盤卷被分紅128MB的塊組。當在根目錄中建立目錄時,inode分配器掃描塊組並將新目錄放到它找到的使用負荷最小的塊組中。這能夠保證目錄在磁盤上的分散性。
(6) 即便上述機制無效,仍然可使用e4defrag整理碎片文件。
1.7 超級塊
超級塊記錄整個文件系統的大量信息,如數據塊個數、inode個數、支持的特性、管理信息,等待。
若是設置sparse_super特性標誌,超級塊和塊組描述符表的冗餘備份僅存放在編號爲0或三、五、7的冪次方的塊組中。若是未設置sparse_super特性標誌,冗餘備份存在與全部的塊組中。如下是2.6.32.18內核中對Ext4超級塊的描述:
3.0的內核中,Ext4的超級塊加入瞭如下相關元數據:快照、文件系統錯誤處理相關、掛載選項、配額文件inode、超級塊校驗和等,見下圖。目前沒有深刻研究這些新的元數據。
1.8 塊組描述符
一個塊組中,具備固定位置的數據結構是超級塊和塊組描述符。其餘數據結構位置均可以不固定。Flex_bg機制使用這個性質將幾個塊組聚合成一個flex塊組,將flex_bg中全部位圖和inode 表放到flex_bg的第一個塊組中。詳細狀況能夠參考個人上一篇Ext4分析博文的Flexible 塊組(flex_bg)部分。
若是設置了meta_bg特性標誌,幾個塊組結合成一個meta group。在meta_bg的狀況下,在meta group中的第一個和最後兩個塊組中僅包含meta group中的塊組的塊組描述符。Flex_bg和Meta_bg互斥於是不能共同出現。
1.9 數據塊位圖與inode位圖
數據塊位圖跟蹤塊組中數據塊使用狀況。Inode位圖跟蹤塊組中Inode使用狀況。每一個位圖一個數據塊,每一位用0或1表示一個塊組中數據塊或inode表中inode的使用狀況。若是一個數據塊大小是4KB的話,那一個位圖塊能夠表示4*1024*8個數據塊的使用狀況,這也是單個塊組具備的最大數據塊個數。這樣能夠算出一個塊組大小是128MB。固然一個位圖塊也能夠表示4*1024*8個inode的使用狀況,可是實際上一個塊組中即便存滿了文件,也不會用到這麼多的inode,由於實際系統中基本不會出現全部文件大小都小於等於1個數據塊大小的狀況。實際上一個塊組中有多少個inode,在塊組描述符中是肯定的,在文件系統格式化過程當中也會看到這個數值,若是沒記錯的話,大概是每4個仍是8個數據塊分配一個inode空間。
1.10 Inode表
爲了找到與一個文件相關的信息,必須遍歷目錄文件找到與文件相關的目錄項,而後加載inode找到該文件的元數據。Ext4在目錄項中用一位存儲了文件類型(一般存儲在inode中)的拷貝,這對性能提高有益。Inode表的大小爲ext4_super_block.s_inode_size * ext4_super_block.s_inodes_per_group Bytes。
Ext4的inode的數據結構大小爲156 bytes,可是Ext4的標準inode的大小是256 bytes。
1.11 查找inode
每一個塊組包含ext4_super_block.s_inodes_per_group個inodes。由於0號inode不存在,能夠經過以下的算式計算inode所在的塊組:
bg=(inode_num -1)/ ext4_super_block.s_inodes_per_group
inode在塊組中inode表中的索引index能夠經過以下的算式計算:
index=(inode_num -1) % ext4_super_block.s_inodes_per_group
inode在inode表中的地址偏移爲:
offset=index * ext4_super_block.s_inode _size
1.12 inode.i_block0[]s的內容
取決於文件類型,inode.i_blocks[]使用的方式不一樣。通常來講,常規文件和目錄用inode.i_blocks[]做爲文件數據塊索引信息,特殊文件將inode.i_blocks[]用於特殊用途。常規文件用inode.i_blocks[]做爲文件數據塊索引信息的三級索引結構會在後面直接、間接塊地址中詳細介紹。
1.13 符號連接
若是符號連接的目標字符串長度小於60字節,那麼就將其存儲在inode.i_blocks[]中,inode中inode.i_blocks[]佔據的大小恰好是60KB。這裏要注意到的是,有些文件其內容是跟文件的元數據放在一塊兒的,於是就沒有了數據塊。也就是說不是每一個文件數據都必然佔據着一個數據塊。
1.14 直接/間接塊地址
Ext2/Ext3中數據塊映射方式以下表
1.15 Extent 樹
Ext4中用extent樹代替了邏輯塊映射。使用extents,用一個struct ext4_extent結構就能夠映射多個數據塊,減小元數據塊的使用。若是設置了flex_bg,甚至能夠用一個extent分配一個很是大的文件。使用extent特性,inode必須設置extents flag。
Extents以樹的方式安排。Extent樹的每一個節點都以一個ext4_extent_header開頭,若是節點是內部節點(ext4_extent_header.eh_depth>0),ext4_extent_header後面緊跟的是ext4_extent_header .eh_entries個索引項struct ext4_extent_idx,每一個索引項指向該extent樹中一個包含更多的節點的數據塊。若是節點是葉子節點(ext4_extent_header.eh_depth==0),ext4_extent_header後面緊跟的是ext4_extent_header .eh_entries個struct ext4_extent數據結構。這些ext4_extent結構指向文件數據塊。Extent樹的根結點存儲在inode.i_blocks中,能夠存儲文件的前4個extents而不需額外的元數據塊。
ext4_extent_header:
struct ext4_extent_idx:extent樹的內部節點,也稱爲索引節點。
ext4_extent:extent樹的葉子節點。
1.16 Extent樹數據塊校驗和:可能加入的新元數據
因爲extent樹的根在inode中,於是Extent樹數據塊指extent樹的除根據節點外的全部內部節點和葉子節點。Extent的樹根節點和葉子節點的數據塊中存儲完xt4_extent_idx和xt4_extent數據結構後至少會留下4 ((2^x%12)>=4) bytes的空間。於是能夠加入一個結構struct ext4_extent_tail,其中存儲32位的校驗和。位於inode中的4個extents無需校驗和,由於inode已經作了校驗和。
1.17 目錄項
Ext4文件系統中,一個目錄差很少是一個平面文件,映射任意長度的字符串到文件系統中的一個inode。文件系統中存在多個目錄項引用同一個inode——硬連接,這也是硬連接不能連接其餘文件系統中的文件的緣由。
1.18 線性(經典)目錄
缺省地,目錄文件中包含一個線性的目錄項數組。未使用的目錄項標記爲inode =0。Ext4文件系統默認地使用struct ext4_dir_entry_2記錄目錄項,除非沒有設置filetype特性標誌。在沒有設置filetype特性標誌的狀況下,使用struct ext4_dir_entry記錄目錄項。
1.19 哈希樹目錄
線性目錄項不利於系統性能提高。於是從ext3開始加入了快速平衡樹哈希目錄項名稱。若是在inode中設置EXT4_INDEX_FL標誌,目錄使用哈希的B樹(hashed btree ,htree)組織和查找目錄項。爲了向後只讀兼容Ext2,htree實際上隱藏在目錄文件中。
Ext2的慣例,樹的根老是在目錄文件的第一個數據塊中。「.」和「..」目錄項必須出如今第一個數據塊的開頭。於是這兩個目錄項在數據塊的開頭存放兩個struct ext4_dir_entry_2結構,且它們不存到樹中。根結點的其餘部分包含樹的元數據,最後一個hash->block map查找到htree中更低的節點。若是dx_root.info.indirect_levels不爲0,那麼htree有兩層;htree根結點的map指向的數據塊是一個內部節點,由一個minor hash索引。Htree中的內部節點的minor_hash->block map以後包含一個零化的(zeroed out) structext4_dir_entry_2找到葉子節點。葉子節點包括一個線性的struct ext4_dir_entry_2數組;全部這些項都哈希到相同的值。若是發生溢出,目錄項簡單地溢出到下一個葉子節點,哈希的least-significant位(內部節點的map)作相應設置。
以htree的方式遍歷目錄,計算要查找的目錄文件名稱的哈希值,而後使用哈希值找到對應的數據塊號。若是樹是flat,該數據塊是目錄項的線性數組,於是可被搜索到;不然,計算文件名稱的minor hash,並使用minor hash查找相應的第三個數據塊號。第三個數據塊是目錄項線性數組。
Htree的根 :struct dx_root
Htree的內部節點: struct dx_node
Htree 樹根和節點中都存在的 Hash map: struct dx_entry
1.20 擴展屬性EA
擴展屬性(xattrs)一般存儲在磁盤上的一個單獨的數據塊中,經過inode.i_file_acl*引用。擴展屬性的第一應用是存儲文件的ACL以及其餘安全數據(selinux)。使用user_xattr掛載選項就可爲用戶存儲以「user」開頭的全部擴展屬性。這樣的限制在3.0內核中已經消失。
能夠在兩個地方找到擴展屬性:一是在一個inode項結尾到下一個inode項開頭的地方;二是inode.i_file_acl指向 的數據塊之中,到3.0爲止,這個數據塊中不包含指向第二個擴展屬性數據塊的指針。理論上能夠將每一個屬性值存儲到一個單獨的數據塊中,可是3.0內核爲止仍然沒有這樣作。
當擴展屬性不存儲在一個inode以後的時候,就會有一個頭部ext4_xattr_ibody_header
擴展屬性數據塊的開頭是ext4_xattr _header
緊跟在ext4_xattr_ibody_header或者ext4_xattr _header後面的是結構數組 struct ext4_xattr_entry
擴展屬性值能夠緊跟在ext4_xattr_entry項表後面。考慮4 bytes對齊。擴展屬性值從擴展屬性數據塊的末尾開始向ext4_xattr _header / ext4_xattr_entry表的方向增加。當發生溢出時,溢出的部分放到一個單獨的磁盤數據塊上。
1.21 日誌(JBD2)
文件系統在磁盤上保留一段小的連續區域(默認128MB),做爲儘量須要快速寫入磁盤的「重要」數據的存放地。一旦該重要數據事務徹底寫到磁盤,將其從磁盤寫緩存中刷出。被提交的數據一份記錄也被寫到日誌。一段時間後,日誌在擦除提交記錄前將事務寫到它們在磁盤上的最終位置(可能包含大量的尋道或者大量的讀-寫-擦除)。
從性能方面考慮,Ext4默認直接將文件系統元數據寫到日誌。於是不能保證文件數據塊的一致性。
日誌的inode爲8。日誌inode的前68 bytes複製了ext4 超級塊。日誌文件在文件系統中是普通文件,可是隱藏不可見。日誌文件一般消耗一個完整的塊組,能夠經過mke2fs將日誌文件放在磁盤的中間。
Ext4和Ocfs2都使用JBD2。
1.21.1 佈局
日誌佈局
一個事務以描述符和一些數據或者block revocation鏈表開始。一個結束的事務老是以一個提交塊結束。若是沒有提交記錄(或者校驗和不匹配),事務在日誌重演的時候將被丟棄。
1.21.2 數據塊頭部
日誌中的每一個數據塊的開頭都是一個12 bytes的數據結構 struct journal_header_s
1.21.3 超級塊
日誌的超級塊比Ext4的超級塊簡單。保存在日誌的超級塊中是日誌的關鍵數據。日誌超級塊使用數據結構struct journal_superblock_s表示,大小爲1024 bytes。
1.21.4 描述數據塊Descriptor Block
Descriptor Block包含一個日誌數據塊tags的數組,這些tags描述了日誌中接下來的數據塊的最終位置。
日誌數據塊tags具備以下格式:由數據結構struct journal_block_tag_s表示,能夠是8,12,24或38bytes。
1.21.5 數據塊Data Block
存放的是經過日誌寫到磁盤的數據塊。可是若是數據塊的前4 bytes與jbd2的魔數匹配,那麼這些4 bytes用0代替,而且在Descriptor Block中設置escaped。
1.21.6 Revocation Block
Revocation block用於記錄本事務中的數據塊鏈表,取代任何潛在日誌中的更陳舊的數據塊這樣能夠加速恢復,由於陳舊的數據塊沒必要寫到磁盤。
Revocation block使用 structjbd2_journal_revoke_header_s結構表示
1.21.7 提交塊
提交快代表了一個事務已完整寫到日誌。一旦提交塊到達日誌,存儲在該事務中的數據能夠寫到它們在磁盤中的最終位置。
提交快由數據結構struct commit_header表示: