F2FS源码分析-2.1 [F2FS 读写部分] F2FS文件数据组织方式以及物理地址的映射
F2FS源碼分析系列文章
主目錄
一、文件系統布局以及元數據結構
二、文件數據的存儲以及讀寫
三、文件與目錄的創建以及刪除(未完成)
四、垃圾回收機制
五、數據恢復機制
六、重要數據結構或者函數的分析
文件數據的保存以及物理地址的映射
文件數據的組織方式一般時被設計為inode-data模式,即 每一個文件都具有一個inode,這個inode記錄data的組織關系,這個關系稱為文件結構。例如用戶需要訪問A文件的第1000個字節,系統就會先根據A文件的路徑找到的A的inode,然后從inode找到第1000個字節所在的物理地址,然后從磁盤讀取出來。那么F2FS的文件結構是怎么樣的呢?
如上圖,F2FS中的一個inode,包含兩個主要部分: metadata部分,和數據塊尋址部分。我們重點觀察數據塊尋址部分,分析inode時如何將數據塊索引出來。在圖中,數據塊尋址部分包含direct pointers,single-indirect,double-indirect,以及triple-indirect。它們的含義分別是:
direct pointer: inode內直接指向數據塊(圖右上角Data)的地址數組,即inode->data模式。
single-indirect pointer: inode記錄了兩個single-indirect pointer(圖右上角Direct node),每一個single-indirect pointer存儲了多個數據塊的地址,即inode->direct_node->data模式。
double-indirect: inode記錄了兩個double-indirect pointer(圖右上角indirect node),每一個double-indirect pointer記錄了許多single-indirect pointer,每一個single-indirect pointer指向了數據塊,即inode->indirect_node->direct_node->data模式。
triple-indirect: inode記錄了一個triple-indirect pointer(圖右上角indirect node),每一個triple-indirect pointer記錄了許多double-indirect pointer,每一個double-indirect pointer記錄了許多single-indirect pointer,最后每一個single-indirect pointer指向了數據塊。即inode->indirect_node->indirect_node->direct_node->data模式。
因此,我們可以發現,F2FS的inode結構采取indirect_node,首先在inode內部尋找物理地址,如果找不到再去direct_node找,層層深入。
f2fs_node的結構以及作用
根據上面的分析,我們可以發現一個對于一個較大的文件,它可能包含inode以外的node,去保存一些間接尋址的信息。single-indirect pointer記錄的是數據塊的地址,而double-indirect pointer記錄的是single-indirect pointer的地址,triple-indirect pointer記錄的double-indirect pointer地址。在F2FS中,
inode對應的是f2fs_inode結構,包含了多個direct pointer指向數據塊物理地址;
single-indirect pointer對應的是direct_node結構,包含了多個direct pointer指向物理地址;
double-indirect pointer對應的是indirect_node結構,包含了多個指向direct_node的地址;
triple-indirect pointer對應的也是indirect_node結構,包含了多個指向indirect_node的地址
接下來我們逐個分析F2FS每一個node的具體數據結構。
基本node結構
為了方便F2FS的對node的區分和管理,f2fs_inode和direct_node以及indirect_node都使用了同一個數據結構f2fs_node進行描述,并通過union的方式,將f2fs_node初始化成不同的node形式,它的結構如下:
struct f2fs_node {union {struct f2fs_inode i;struct direct_node dn;struct indirect_node in;};struct node_footer footer; // footer用于記錄node的類型 } __packed;struct node_footer {__le32 nid; /* node id */__le32 ino; /* inode nunmber */__le32 flag; /* include cold/fsync/dentry marks and offset */__le64 cp_ver; /* checkpoint version */__le32 next_blkaddr; /* next node page block address */ } __packed;其中起到區分是哪一種node的關鍵數據結構是node_footer。如果node_footer的nid和ino相等,則表示這是一個f2fs_inode結構,如果不相等,則表示這是一個direct_node或者indirect_node。
f2fs_inode結構
我們先看f2fs_inode的結構,省略其他元數據的信息,重點關注文件如何索引的,結構如下:
struct f2fs_inode {...__le32 i_addr[DEF_ADDRS_PER_INODE]; // DEF_ADDRS_PER_INODE=923__le32 i_nid[DEF_NIDS_PER_INODE]; // DEF_NIDS_PER_INODE=5... } __packed;i_addr數組就是前面提及的direct pointer,數組的下標是文件的邏輯位置,數組的值就是flash設備的物理地址。例如文件的第一個頁就對應i_addr[0],第二個頁就對應i_addr[1],而i_addr[0]和i_addr[1]所記錄的物理地址,就是文件第一個頁(page)和第二個頁的數據的物理地址,系統可以將兩個物理地址提交到flash設備,將數據讀取出來。
我們可以發現i_addr的數組長度只有923,即一個f2fs_inode只能直接索引到923個頁/塊的地址(約3.6MB),對于大于3.6MB的文件,就需要使用間接尋址。f2fs_inode的i_nid數組就是為了間接尋址而設計,i_nid數組是一個長度為5的數組,可以記錄5個node的地址。其中
i_nid[0]和i_nid[1]記錄的是direct_node的地址,即對應前述的single-indirect pointer。
i_nid[2]和i_nid[3]記錄的是indirect_node的地址,這兩個indirect_node記錄的是direct_node的地址,即對應前述的double-indirect pointer。
i_nid[4]記錄的是indirect_node的地址,但是這個indirect_node記錄的是indirect_node的地址,即前述的triple-indirect pointer。
direct_node和indirect_node結構
direct_inode以及indirect_inode的結構如下所示,direct_node記錄的是數據塊的地址,indirect_inode記錄的是node的id,系統可以通過nid找到對應的node的地址。
struct direct_node {__le32 addr[ADDRS_PER_BLOCK]; // ADDRS_PER_BLOCK=1018 } __packed;struct indirect_node {__le32 nid[NIDS_PER_BLOCK]; // NIDS_PER_BLOCK=1018 } __packed;Wandering Tree問題
在第一章的第一節提到,F2FS的設計是為了解決wandering tree的問題,那么現在的設計是如何解決這個問題的呢。假設一個文件發生更改,修改了direct_node里面的某一個block的數據,根據LFS的異地更新特性,我們需要給更改后的數據一個新的block。傳統的LFS需要將這個新的block的地址一層層網上傳遞,直到inode結構。而F2FS的設計是只需要將direct_node對應位置的addr的值更新為新block的地址,從而沒必要往上傳遞,因此解決了wandering tree的問題。
普通文件數據的保存
從上節描述可以知道,一個文件由一個f2fs_inode和多個direct_inode或者indirect_inode所組成。當系統創建一個文件的時候,它會首先創建一個f2fs_inode寫入到flash設備,然后用戶往該文件寫入第一個page的時候,會將數據寫入到main area的一個block中,然后將該block的物理地址賦值到f2fs_inode->i_addr[0]中,這樣就完成了Node-Data的管理關系。隨著對同一文件寫入的數據的增多,會逐漸使用到其他類型的node去保存文件的數據。
經過上面的分析,我們可以計算F2FS單個文件的最大尺寸:
可以得到如下計算公式:
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字節。
文件讀寫與物理地址的映射的例子
Linux的文件是通過page進行組織起來的,默認page的size是4KB,使用index作為編號。
一個小文件訪問例子
例如一個size=10KB的文件,需要3個page去保存數據,這3個page的編號是0,1,2。當用戶訪問這個文件的第2~6kb的數據的時候,系統就會計算出數據保存在page index = 0和1的page中,然后根據文件的路徑找到對應的f2fs_inode結構,page index = 0和1即對應f2fs_inode的i_addr[0]和i_addr[1]。系統進而從這兩個i_addr讀取物理地址,提交到flash設備將數據讀取出來。
一個大文件訪問例子
假設用戶需要讀取文件第4000個頁(page index = 3999)的數據,
第一步: 那么首先系統會根據文件路徑找到對應的f2fs_inode結構
第二步: 由于4000 >(923 + 1018 + 1018),f2fs_inode->i_addr和f2fs_inode->nid[0]和nid[1]都無法滿足需求,因此系統根據f2fs_inode->nid[2]找到對應的 indirect_node的地址
第三步: indirect_node保存的是direct_node的nid數組,由于 4000 - 923 - 1018 - 1018 = 1041,而一個direct_node只能保存1018個block,因此可以知道數據位于indirect_node->nid[1]對應的direct_node中
第四步: 計算剩下的的偏移(4000-923-1018-1018-1018=23)找到數據的物理地址位于該direct_node的direct_node->addr[23]中。
總結
以上是生活随笔為你收集整理的F2FS源码分析-2.1 [F2FS 读写部分] F2FS文件数据组织方式以及物理地址的映射的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 金蝶云星空与旺店通集成解决方案(金蝶主管
- 下一篇: liferay mysql_Lifera