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