概述
XFS为用户呈现一个标准的Unix文件系统接口:一棵由directory、file、symbolic link、device组成的树,这些实体在文件系统内部均由一个index node表示(即inode),旧版本inode大小为256字节,V5之后改成512B~2KB,默认512B,可以在mkfs时指定大小,一个inode number唯一引用一个inode。XFS内被切分成若干相等的chunk(16MB到1TB),称为Allocation Group(即AG),几乎可以认为AG就是一个管理自己空间、inode以及其他次要元数据的独立文件系统。当并发访问量增加的时候,拥有多个AG使得XFS可以并行处理大部分操作而不会降低性能,AG使用多个B+ tree维护空闲块位置、已分配inode位置、空闲inode位置等的记录。
一个AG通常具有如下特征:
-
一个描述文件系统整体信息的super block;
-
inode分配与跟踪;
-
逆block-map索引(选配);
-
data block引用计数索引(选配);
AG组成
执行mkfs.xfs之后,AG的disk layout如下:
通常称XFS的第一个AG为primary AG,primary AG的super block维护着文件系统的全局信息,其他secondary AG的super block仅供xfs_repair使用。若super block中的XFS_SB_VERSION2_LAZYSBCOUNTBIT标记被置位,则仅在umount或shutdown更新disk对应内容。AG将空间划分为若干个相等大小的block并编号(File System Block,即FSB),AG使用两个B+tree跟踪free space,一个将block number做为key,一个将block count做为key,这种机制使得XFS能够快速找到接近指定block number或size的free space。AG使用一个free list为free space的两个B+tree预留空间,通常是4个block,耗尽则追加。AG基于可容纳64个inode的chunk分配inode,使用一个B+tree跟踪所有chunk中inode的分配与回收(即inode B+Tree),若设置了
XFS_SB_FEAT_RO_COMPAT_FINOBT属性,则会启用第二个B+tree跟踪那些包含空闲inode的chunk(即free inode B+tree),用于加速inode分配。
inode number编码机制
XFS将inode在磁盘上的物理位置编码得到inode number,inode number有两种形式:
-
AG relative:通常是32bit以内,该AG中的FSB编号+该FSB中的inode编号;
-
absolute:通常是64bit,AG编号+该AG中的FSB编号+该FSB中的inode编号;
AG inode管理结构
AG管理自己的inode,第三个sector包含inode的相关信息(即AGI),其结构如下:
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];
/*
* v5 filesystem fields start here; this marks the end of logging region 1
* and start of logging region 2.
*/
uuid_t agi_uuid;
__be32 agi_crc;
__be32 agi_pad32;
__be64 agi_lsn;
__be32 agi_free_root;
__be32 agi_free_level;
}
关键成员含义如下:
agi_count表示AG中已分配inode个数;
agi_freecount表示AG中空闲inode个数;
agi_newino表示最近分配chunk的AG-relative inode number;
agi_unlinked[64]是一个hash table,保存已经unlinked且仍被引用的inode;
agi_root指向inode B+tree;
agi_free_root指向free inode B+tree;
inode B+tree
末端leaf
leaf包含一个使用如下结构的数组:
struct xfs_inobt_rec {
__be32 ir_startino;
__be32 ir_freecount;
__be64 ir_free;
};
关键成员含义如下:
ir_startino:该chunk中的最小inode number;
ir_freecount:该chunk中的空闲inode个数;
ir_free:表示该chunk中哪些inode空闲的64位位图 ;
中间node
node包含一个key/pointer对的数组:
struct xfs_inobt_key {
__be32 ir_startino;
};
typedef __be32 xfs_inobt_ptr_t;
关键成员含义如下:
ir_startino:leaf中所有record的最小inode number;
inobt_ptr_t:指向存放leaf的AG block number;
单层inode B+tree
两层inode B+tree
sparse inode特性
综上所述,由于inode分配是基于容纳64个inode的chunk,若没有与chunk大小匹配的空闲extent,则inode分配就会失败,XFS认为已经耗尽space,在一个free space高度碎片化的文件系统中,这会导致实际耗尽free space之前就产生耗尽space错误,故而引入sparse inode特性,利用xfs_inobt_rec结构中freecount字段里早前没有用的bit,跟踪inode B+tree里一部分未分配给inode使用的chunk,使得XFS在必要的时候可以基于一个block分配inode。
leaf
leaf包含一个使用如下结构的数组:
struct xfs_inobt_rec {
__be32 ir_startino;
__be16 ir_holemask;
__u8 ir_count;
__u8 ir_freecount;
__be64 ir_free;
};
关键成员含义如下:
ir_startino:该chunk中的最小inode number,按64向下取整;
ir_holemask:表示该chunk中哪部分未分配给inode的16位位图,一个bit代表四个inode,若某个bit标记,则ir_free对应bit也必须被标记;
ir_count:该chunk中实际可分配inode总数;
ir_freecount:该chunk中空闲inode个数;
ir_free:表示该chunk中哪些inode对于分配是无效的;
举例
在该单层inode B+tree中,红线标记leaf中的第二条记录,通过inode number 14912可以得到chunk位置,0xff即0x00ff,表明该chunk前32个inode大小的空间并没有用来放inode,32表明该chunk实际可分配inode总数,0表明该chunk没有空闲inode,0xffffffff即0x00000000ffffffff,表明没有可分配inode。该chunk实际上第一个inode的inode number为14944,14912~14943由于空间已经被其他数据占用,不曾分配给inode使用,从另一个角度看,对于分配操作来说,它们确实是“free”的,后续若释放了该空间,XFS是否会修改inode B+tree的ir_holemask字段,让它们变成真正的free呢?