F2FS 基础知识二
1.文件索引樹結構
大多數現代文件系統都喜歡使用 B-trees 或類似的結構來管理索引(index)以定位文件中的 blocks。大多數文件系統中通過使用“extents”來減少文件數據塊的總索引大小。F2FS 不采用 B-tree 結構管理索引,也不使用extents減少文件數據塊索引的大小(雖然F2FS為每個節點(node)維護一個extent作為提示)
所謂的extent指的是一段連續的物理磁盤塊,這樣,只需要一個extent數據結構我們就能描述一段很長的物理磁盤空間,性價比很高。ext4文件系統中的extent數據結構的主要作用是索引,即根據邏輯塊號查詢文件的extent能夠準確定位邏輯塊對應的物理塊號,另外,ext4的extent在文件較小的時候存儲在inode的i_data[]中,在文件較大的時候,所有的extent會被組織成一棵B+樹
2.索引節點
管理數據位置的關鍵數據結構是“node”,與傳統文件系統相似,F2FS 使用三種 node:inode,直接 node,間接 node。F2FS 分配4 KB 的空間給一個 inode,其中包括923個數據塊索引指針,兩個一級索引塊(直接 node )指針,兩個二級索引塊(間接 node )指針,以及一個三級索引塊(二級間接 node)指針。一個一級索引塊(直接 node )包含1018個數據塊指針,一個二級索引塊(間接 node )包含1018個一級索引塊(直接 node )指針,一個三級索引塊(二級間接 node)包含1018個二級索引塊(間接 node )指針。因此一個文件的最大大小是:
4 KB * (923 + 21018 + 210181018 + 10181018*1018) := 3.93 TB
3.文件數據塊索引
文件系統一個重要功能是實現文件的保存以及讀寫。每個文件系統有不同的保存和讀寫方式,而F2FS則是通過Node-Data的形式將數據組織起來。其中最重要的結構就是f2fs_node結構,它實現了文件數據的組織和管理。
f2fs_node的結構以及作用
F2FS每一個文件,根據文件大小的不同,對應了一個或者多個f2fs_node結構,它實際存在于磁盤當中,并每一個f2fs_node結構都占據了4KB的磁盤空間,它的結構如下:
普通文件數據的保存
從上節描述可以知道,一個文件由一個f2fs_inode和多個direct_inode或者indirect_inode所組成。當系統創建一個文件的時候,它會首先創建一個f2fs_inode寫入到磁盤,然后用戶往該文件寫入第一個page的時候,會將數據寫入到main area的一個block中,然后將該block的物理地址賦值到f2fs_inode->i_addr[0]中,這樣就完成了Node-Data的管理關系。隨著對同一文件寫入的數據的增多,會逐漸使用到其他類型的node去保存文件的數據。
根據f2fs_inode能夠保存的數據的變量f2fs_inode->i_addr以及f2fs_inode->i_nid,可以計算F2FS最大允許單個文件的大小:
a). f2fs_inode 直接保存了923個頁的數據的物理地址b). f2fs_inode->i_nid[0~1] 保存了兩個 direct_node 的地址,因此可以通過二級索引的方式訪問數據,因此這里可以保存 2 x 1018個頁的數據c). f2fs_inode->i_nid[2~3] 保存了兩個indirect_node 的地址,這兩個其中2個indirect_node保存的是 direct_node 的nid,因此可以通過二級索引的方式訪問數據,因此可以保存 2 x 1018 x 1018個頁的數據;d). f2fs_inode->i_nid[4] 保存了一個indirect_node 的地址,這個indirect_node保存的是 indirect_node 的nid,因此可以通過三級索引的方式訪問數據,因此可以保存 1018 x 1018 x 1018個頁的數據可以得到如下計算公式: 4KB x (923 + 2 x 1018 + 2 x 1018 x 1018 + 1 x 1018 x 1018 x 1018) = 3.93TB 因此F2FS單個文件最多了保存3.93TB數據。
內聯文件數據的保存
從上節可以知道,文件的實際數據是保存在f2fs_inode->i_addr對應的物理塊當中,因此即使一個很小的文件,如1個字節的小文件,也需要一個node和data block才能實現正常的保存和讀寫,也就是需要8KB的磁盤空間去保存一個尺寸為1字節的小文件。而且f2fs_inode->i_addr[923]里面除了f2fs_inode->i_addr[0]保存了一個物理地址,其余的922個i_addr都被閑置,造成了空間的浪費。
因此F2FS為了減少空間的使用量,使用內聯(inline)文件減少這些空間的浪費。它的核心思想是當文件足夠小的時候,使用f2fs_inode->i_addr數組直接保存數據本身,而不單獨寫入一個block中,再進行尋址。因此,如上面的例子,只有1個字節大小的文件,只需要一個f2fs_inode結構,即4KB,就可以同時將node信息和data信息同時保存,減少了一半的空間使用量。
根據上述定義,可以計算得到多大的文件可以使用內聯的方式進行保存,f2fs_inode有尺寸為923的用于保存數據物理地址的數組i_addr,它的數據類型是__le32,即4個字節。保留一個數組成員另做它用,因此內聯文件最大尺寸為: 922 * 4 = 3688字節。
目錄
LFS(Log-structured File System) 實際上對目錄布局沒有強加任何特殊的要求,因而訪問 F2FS 文件系統的目錄等同于訪問其他文件系統的目錄。目錄的主要目的是提供更快速的文件名查找,為每個可使用 telldir() 報告的文件名提供穩定的地址。
原始的 Unix 文件系統支持256字節的文件名長度,使用與 Ext2 相同的目錄機制——在布滿目錄項的文件中順序遍歷查找文件名。這種方法簡單有效,但是對于大目錄的擴展性不好。
多數現在主流文件系統如 Ext3,XFS 和 Btrfs 使用多種機制(包括 B-tree),有時通過哈希索引文件名。B-tree 的一個問題是節點(nodes)需要切分,這導致一些目錄項在文件中頻繁移動,這給 telldir() 提供文件名對應的穩定地址帶來了額外的挑戰。這大概就是 telldir() 經常被認為是不太好的接口的主要原因。
4.1 目錄結構
一個目錄項占據11 Bytes,也就是包含以下屬性:
(1) – hash 文件名的哈希值 (2) – ino inode 號 (3) – len 文件名長度 (4) – type 文件類型,如目錄,符號鏈接,等等一個目錄項 block 包含214個目錄項槽位(slot)和文件名,其中用一個 bitmap 表示每個目錄項是否有效,1個目錄項塊占用 4 KB,結構如下:
Dentry block(4 Kb) = bitmap(27bytes) + Reserved(3bytes) + Dentries(11214bytes) + filename(8214bytes)
4.2 目錄哈希
F2FS 使用一些連續搜索和哈希方法提供一個簡單的搜索機制,合理高效,簡單實現提供穩定的 telldir() 的地址。F2FS 文件系統的大多數的哈希算法代碼都是參考 Ext3,但是刪除(忽略)了使用每個目錄作為seed。F2FS 的 seed 使用一個秘密的隨機數,以確保滿足使用的哈希值在每個目錄中都不同,因而是不可預測的。使用這樣的 seed 可以給哈希沖突提供保護。雖然哈希沖突在現實中是不可避免的,但使用這種方式可以很容易避免這種沖突,這種刪除(忽略) 使用每個目錄作為seed 帶來的效果很意外。
很容易想到目錄結構是一系列的哈希表連續地存儲在一個文件中,每個哈希表具有一組相當大的 buckets。查找過程從第一個哈希表到下一個哈希表,在每一個階段對合適的bucket 執行線性查找,直到找到文件名或者查找到最后的那個哈希表。在查找過程中,在合適的 bucket 中的任意空閑空間都會被記錄,以防需要創建文件名的時候用到。
F2FS 對目錄結構實現了多級哈希表,每一級都有一個使用專用數字的哈希 bucket 的哈希表,如下所示。注意,“A(2B)”表示一個哈希 bucket 包含兩個數據塊。
---------------------- A : bucket 哈希bucket B : block 數據塊 N : MAX_DIR_HASH_DEPTH 最大目錄哈希層級 ---------------------- level #0: A(2B) level #1: A(2B) - A(2B) level #2: A(2B) - A(2B) - A(2B) - A(2B). . . . . level #N/2: A(2B) - A(2B) - A(2B) - A(2B) - A(2B) - ... - A(2B). . . . . level #N: A(4B) - A(4B) - A(4B) - A(4B) - A(4B) - ... - A(4B)所有的哈希表按如下方式組織:第一個哈希表恰好有1個 bucket,大小是兩個數據塊,因而對于最開始的少數幾百個目錄項,使用簡單的線性搜索。第二個哈希表有2個 bucket,第三個哈希表有4個 bucket,第四個哈希表有8個 bucket,然后是16 個 bucket,……,直到第31個哈希表有大約10億(2^30)個 bucket,每個 bucket 都是兩個數據塊大小。
F2FS 在目錄中查找文件名時,首先計算文件名的哈希值,然后在 level #0 中掃描哈希值查找包含文件名和文件的 inode 的目錄項。如果沒有找到,F2FS在 level #1 中掃描下一個哈希表,用這種方式,F2FS 以遞增的方式掃描每個 level 的哈希表(如果上一 level 中沒有查找到結果),在每個 level中,F2FS 僅需要掃描一個bucket,而該 bucket 的號是由文件名的哈希值與該 level 中的 buckets 個數的相除取余得到的。也就是每個 level 中需要掃描的一個 bucket 由下式確定的,查找復雜度是 0:
bucket number to scan in level #n = (hash value) % (# of buckets in level #)
所以哈希表的最終結果就是需要線性搜索幾百個目錄項,如果目錄項很大的話,需要通過幾個數據塊的搜索。搜索長度僅隨著目錄文件中目錄項的個數對數級增長,所以容易擴展。這種搜索當然比完全的線性搜索算法要好,但是看起來好像比實際需要做的工作更多。但是它保證了每次加入或刪除一個文件名的時候僅需要更新一個數據塊,因為目錄項沒有移動,目錄項在文件中的偏移對于 telldir() 來說就是一個穩定的地址,這是非常有意義的特性。
在創建文件的情況下,F2FS 在哈希表的buckets 中找到該創建文件名對應的空的連續的槽位(slots)(這一句的原文是:F2FS finds empty consecutive slots that cover the file name)。F2FS 自哈希表的 level #0 到level #N 從哈希表中給搜索該空閑的槽位,與查找文件系統中已有文件的查找操作一樣。
總結
以上是生活随笔為你收集整理的F2FS 基础知识二的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在线图片批量压缩工具
- 下一篇: HDU 5514 Frogs 容斥