關於XFS文件系統概述

前言: 目前XFS已成爲Linux主流的文件系統,所以有必要了解下其數據結構和原理。

XFS文件系統

XFS是一個日誌型的文件系統,能在斷電以及操作系統崩潰的情況下保證數據的一致性。XFS最早是針對IRIX操作系統開發的,後來移植到linux上,目前CentOS 7已將XFS作爲默認的文件系統。使用XFS已成爲了潮流,所以很有必要了解下其數據結構和原理。

XFS官方說明文檔參考:https://xfs.org/docs/xfsdocs-xml-dev/XFS_Filesystem_Structure//tmp/en-US/html/index.html

接下來將介紹XFS的一些概念,包括分配組、超級塊、inode等等,過程中會結合xfs_db(xfs提供的輸出文件系統信息的工具)打印一些信息,瞭解當前XFS的實時數據。

 

分配組(Allocation Group)

XFS將空間分爲若干個分配組,每個分配組大小相等(最後一個可能不等)。分配組包含有超級塊、inode管理和剩餘空間管理等,所以分配組可以認爲是一個單獨的文件系統。正是分配組這樣的設計,使得XFS擁有了並行IO的能力。在單個分區上使用XFS體現不了這種並行IO能力,但是如果文件系統跨越多個物理硬件比如ceph,並行IO將大大提高吞吐量利用率。

 

上圖爲分配組的結構圖,重點關注前面4個扇區,從上到下分別爲超級塊、空閒塊信息、inode信息和內部空閒列表。

 

超級塊(superblock)

超級塊位於分配組的第一個扇區,包含了分配組和文件系統的全部元數據信息,由於結構體比較大,這裏就不作列舉,可去官方文檔中查看https://xfs.org/docs/xfsdocs-xml-dev/XFS_Filesystem_Structure//tmp/en-US/html/Allocation_Groups.html

xfs_db查看超級塊內容,執行xfs_db -r /dev/xxx(xxx爲XFS所在的分區),輸入sb再輸入p即可,如下圖所示(鑑於篇幅未盡列出輸出):

超級塊有幾個核心的元數據爲:

blocksize:塊大小,一般爲4KB;
dblocks:一個分配組含有的塊數目;
agcount:整個文件系統含有的分配組數目;
sectsize:扇區大小,一般爲512B;
inodesize:inode節點大小,一般爲512B;
icount:整個文件系統目前已經分配的inode數目;
ifree:整個文件系統空閒的inode數目,由於XFS不是格式化的時候預分配所有的inode,而是根據使用情況動態構造inode,所以該值爲動態值。


空閒塊信息(AG free block info)

位於分配組的第二個扇區,主要描述兩個空閒空間B+樹和剩餘空間信息,結構體如下:

typedef struct xfs_agf {
     __be32              agf_magicnum;
     __be32              agf_versionnum;
     __be32              agf_seqno;
     __be32              agf_length;
     __be32              agf_roots[XFS_BTNUM_AGF];
     __be32              agf_spare0;
     __be32              agf_levels[XFS_BTNUM_AGF];
     __be32              agf_spare1;
     __be32              agf_flfirst;
     __be32              agf_fllast;
     __be32              agf_flcount;
     __be32              agf_freeblks;
     __be32              agf_longest;
     __be32              agf_btreeblks;
} xfs_agf_t;


核心成員如下:

agf_roots:XFS_BTNUM_AGF爲2,指明瞭2棵空閒空間B+樹在哪個block,通過查找這兩棵樹找到合適的空閒block;
agf_levels:樹高;
agf_freeblks:分配組目前空閒block數目

xfs_db輸入agf可查看空閒塊信息,如下圖所示:

 

空閒空間B+樹

空閒塊信息包含了兩顆空閒空間B+樹,分別以block序號和block數目爲關鍵字,滿足兩種不同的需求。

B+樹貫徹了整個XFS,對其有所瞭解才能更好的理解XFS的運作,網上有很多關於B+樹的資料,請自行查閱,這裏只描述一些核心概念:

屬於多叉平衡排序樹;

有m個關鍵字的中間節點有m個子節點;

m個子節點的關鍵字集合包含父節點的關鍵字,B+樹有點像跳錶;

中間節點只含有關鍵字不含數據,葉子節點含有所有的關鍵字和數據;

葉子節點含有左右節點指針,所有葉子節點實際上是一條有序鏈表。

 
B+樹在基於磁盤查找的軟件中應用廣泛,如數據庫,文件系統也一樣,這些軟件都要讀取磁盤數據再查找,所以一次讀取儘量多的關鍵字尤爲重要,B+樹的單節點多關鍵字可滿足該需求。

接下來將通過xfs_db去探索這兩棵樹的內容,從agf的打印信息可看到bnoroot=1和cntroot=2,它們分別是以block序號和block數目爲關鍵字的B+樹的根節點所在的block序號。

跟蹤以block序號爲關鍵字的B+樹操作如下:

由於agf中level爲1,所以該B+樹沒有中間節點,直接就是葉子節點,包含有空閒block數據,圖中的recs數組便是,可見目前有3大塊空閒空間。

跟蹤以block數目爲關鍵字的B+樹操作如下:

 

Inode B+樹信息

位於分配組的第三個扇區,主要描述inode B+樹的根block、已構造的inode個數以及空閒個數,數據結構如下:

typedef struct xfs_agi {
     __be32              agi_magicnum;
     __be32              agi_versionnum;
     __be32              agi_seqno
     __be32              agi_length;
     __be32              agi_count;
     __be32              agi_root;
     __be32              agi_level;
     __be32              agi_freecount;
     __be32              agi_newino;
     __be32              agi_dirino;
     __be32              agi_unlinked[64];
} xfs_agi_t;


核心成員如下:

agi_root:inode B+樹的根block;
agi_level:樹高;
agi_count:已構造的inode數目;
agi_freecount:空閒的inode數目。


xfs_db輸入agi可讀取到Inode B+樹信息,如下圖所示:

由上圖可知B+樹的根block爲root=3,跟蹤該block便可找到具體的inode數據。

 

Inode信息

每一個文件或目錄都對應一個inode,用於描述文件的基本信息,除了目錄或鏈接,inode不攜帶文件數據。

inode分爲3部分,如下;

xfs_dinode_core_t:固定信息,描述文件類型、屬性、訪問時間等;
data fork:存放數據位置信息;
extended attribute fork:存放擴展數據位置信息;

 

Inode Core

描述文件的基本信息,數據結構定義如下:

typedef struct xfs_dinode_core {
     __uint16_t    di_magic;    /* inode magic # = XFS_DINODE_MAGIC */
    __uint16_t    di_mode;    /* mode and type of file */
    __int8_t    di_version;    /* inode version */
    __int8_t    di_format;    /* format of di_c data */
    __uint16_t    di_onlink;    /* old number of links to file */
    __uint32_t    di_uid;        /* owner's user id */
    __uint32_t    di_gid;        /* owner's group id */
    __uint32_t    di_nlink;    /* number of links to file */
    __uint16_t    di_projid;    /* owner's project id */
    __uint8_t    di_pad[8];    /* unused, zeroed space */
    __uint16_t    di_flushiter;    /* incremented on flush */
    xfs_timestamp_t di_atime;    /* time last accessed */
    xfs_timestamp_t di_mtime;    /* time last modified */
    xfs_timestamp_t di_ctime;    /* time created/inode modified */
    xfs_fsize_t    di_size;    /* number of bytes in file */
    xfs_drfsbno_t    di_nblocks;    /* # of direct & btree blocks used */
    xfs_extlen_t    di_extsize;    /* basic/minimum extent size for file */
    xfs_extnum_t    di_nextents;    /* number of extents in data fork */
    xfs_aextnum_t    di_anextents;    /* number of extents in attribute fork*/
    __uint8_t    di_forkoff;    /* attr fork offs, <<3 for 64b align */
    __int8_t    di_aformat;    /* format of attr fork's data */
    __uint32_t    di_dmevmask;    /* DMIG event mask */
    __uint16_t    di_dmstate;    /* DMIG state info */
    __uint16_t    di_flags;    /* random flags, XFS_DIFLAG_... */
    __uint32_t    di_gen;        /* generation number */
} xfs_dinode_core_t;


核心成員如下:

di_mode:指定文件類型和訪問權限,具體解析參考https://www.xuebuyuan.com/1106749.html
di_format:指定data fork的數據格式,有以下類型:

typedef enum xfs_dinode_fmt {
     XFS_DINODE_FMT_DEV,     // 用於字符和塊設備
     XFS_DINODE_FMT_LOCAL,   // 用於目錄和鏈接,表明數據就存放在inode的data fork中
     XFS_DINODE_FMT_EXTENTS, // data fork存放extent,extent指向具體的block,block存放數據
     XFS_DINODE_FMT_BTREE,   // data fork存放B+樹根block
     XFS_DINODE_FMT_UUID     // 該值不再使用,忽略
} xfs_dinode_fmt_t;


di_uid:擁有者id;
di_gid:擁有者的組id;
di_atime、di_mtime、di_ctime:訪問時間、修改時間、修改屬性時間;
di_size:數據大小,比如文件的大小;
di_forkoff:實際值要乘以8,是data fork和extended attribute fork的分界線,以data fork爲起始,初始值爲0,代表沒有extend attribute fork。

 

Inode number

Inode有個唯一表明身份的number,有兩種格式:相對格式(32位)和絕對格式(64位)。

從上圖可見,絕對格式比相對格式多了AG number部分,中間部分爲block序號,右側部分爲inode在該block內的序號,可見根據inode number便可得到inode在磁盤的具體位置。sb_agblklog和sb_inoplog的值位於超級塊中。

計算inode所在位置方法:

假設block序號爲A,inode序號爲B,AG number爲C,每個分配組的block數目爲D,Inode大小爲E,block大小爲F(這些值可以計算或通過超級塊得到)

則inode所在的block序號 x = C  D + A,則inode地址爲 y = x  F + E * B

 

data fork

不同文件類型的data fork形式有所不同,同樣類型的文件根據大小也會有不同的數據結構,接下來將一一描述。

普通文件

普通文件的數據不會放在data fork中,視extent數目大小,有兩種數據形式:

Extent List:每個extent指明存放數據的block地址,遍歷該list便可得到全部文件數據;
B+tree Extent List:由於data fork的容量有限,如果extent數量太多,將採用B+樹的形式存放extent,亦即採用額外的block存放extent。

 

目錄

有四種形式的目錄:

Shortform目錄:目錄下文件不多或者文件名短,也就是data fork能容納下文件名和文件inode,則目錄的數據放在data fork中;
Block目錄:data fork存放不下目錄的內容,採用額外的一個block存放;
Leaf目錄:一個block存放不下目錄的內容,把索引信息從block中抽離,索引信息用額外一個block存放;
Node目錄:一個block存放不下索引信息,採用多個block存放索引信息;
B+樹目錄:data fork已經存放不下數據block extent,採用B+樹方式存放block extent。

 

鏈接

有兩種形式的鏈接:

Shortform鏈接:data fork能存放下鏈接的內容,內容即目標文件的路徑;
Extent鏈接:採用額外的block存放鏈接的內容,extent存放block信息。

 

結束語

以上就是XFS文件系統的一些基本數據結構,瞭解了基本的數據結構使我們能更深入的瞭解和探索XFS系統,當系統出現問題時也可以更好地分析原因。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章