Ext4文件系统结构初探(结合Linux指令实践)

目录

引言

主要参考资料:

基本概念:

Block

Group

Inode

实践

查看所有已挂载的设备

查看文件系统的超级块

查看某个小文件的Stat,为何Blocks是8?

查看Group信息

分析INODE结构

读取指定块号的数据

对一个稍大文件的Inode试试手!

对一个更大的文件(~900MB)试手 

分析文件夹结构

待补充的内容


引言

前两天HXD线上面电信天翼云的开发岗,结果问到文件系统有哪几种...一方面感慨现在搞开发必须得上知天文下知地理,另一方面觉得背书终归对能力提升有限。故尝试探讨Linux系统中Ext4文件系统的底层结构。

主要参考资料:

英文文档,详细罗列了底层结构

唯一不足之处是没有图例。

鸟哥的Linux私房菜

结合理论知识与实践的优秀教程,但目前看下来该文没有对Ext4文件系统进行专门的介绍。

基本概念:

Block

A block is a group of sectors between 1KiB and 64KiB, and the number of sectors must be an integral power of 2.

这里sector指的就是簇,它是物理层面上磁盘的最小存储单位。

Group

Blocks are in turn grouped into larger units called block groups.

引用自https://blog.51cto.com/u_15265005:

Ext4文件系统将磁盘空间划分为若干组,以这一组为单位管理磁盘空间,这个组叫做块组(Block Group),块组其内部有元数据来管理这部分区域的磁盘。

Inode

Inode refers to fields in an inode table entry.

在Linux操作系统中文件是由inode标识的,每个文件在磁盘上都有一个inode节点。对于Ext2文件系统来说,通常这些inode节点会相对集中的放在一个区域,这个区域叫做inode表。

实践

查看所有已挂载的设备

#查看文件系统的指令
df -T

#查看盘名
ll /dev/*

查看文件系统的超级块

sudo dumpe2fs -h /dev/sda5
Inode count:              1277952
Block count:              5111040
Reserved block count:     255552
Free blocks:              2173751
Free inodes:              1005093
First block:              0

Block size:               4096
一个Block能存4096bit,block是文件系统中文件存储的基本单位

Fragment size:            4096
Group descriptor size:    64
Reserved GDT blocks:      1024
Blocks per group:         32768
Fragments per group:      32768

Inodes per group:         8192
一个Inode对应一个文件,所以一个组最多能有8192个文件。
一个文件系统可以包含若干个Group,所以Inode count远远大于此处的Inodes per group

Inode blocks per group:   512
Inode太多了所以需要占用多个block。
对于该属性,我的理解是 :Inode blocks per group * (BlockSize/Inode size) = BlockSize
即 512 * ( 4096/256 ) = 8192 

Flex block group size:    16

Filesystem created:       Mon Oct  5 13:24:59 2020
Last mount time:          Thu Sep 16 00:01:46 2021
Last write time:          Thu Sep 16 00:01:42 2021
Mount count:              40
Maximum mount count:      -1
Last checked:             Mon Oct  5 13:24:59 2020
Check interval:           0 (<none>)
Lifetime writes:          88 GB
Reserved blocks uid:      0 (user root)
Reserved blocks gid:      0 (group root)
First inode:              11

Inode size:               256
此处的Inode size指代Inode结点的大小(单位为比特)

Required extra isize:     32
Desired extra isize:      32
Journal inode:            8
First orphan inode:       655386
Default directory hash:   half_md4
Directory Hash Seed:      11778f68-41b9-4d07-9b77-714815ee7721

查看某个小文件的Stat,为何Blocks是8?

linux@ubuntu:~/temp$ cat 1.txt
123456
linux@ubuntu:~/temp$ stat 1.txt
  File: 1.txt
  Size: 7         	Blocks: 8          IO Block: 4096   regular file
Device: 805h/2053d	Inode: 398584      Links: 2
Access: (0664/-rw-rw-r--)  Uid: ( 1000/   linux)   Gid: ( 1000/   linux)
Access: 2021-09-16 04:05:42.486923173 -0700
Modify: 2021-09-11 19:36:52.889907621 -0700
Change: 2021-09-11 19:36:52.889907621 -0700
 Birth: -

解答:https://blog.csdn.net/daiyudong2020/article/details/53897775
Stat指令中对Block的定义是一种单位,等价于512比特大小。
另一方面,上文中Block Size为4096(比特)。两个文件无论大小如何,不会占用同一个Block(换而言之,Block是文件系统中的基本存储单位、不可分割),最后有4096/512=8 。

查看Group信息

Group信息(上文查询‘超级块’的指令会一并返回各个Group的信息):
Group 4: (Blocks 131072-163839) csum 0xfa75 [INODE_UNINIT, ITABLE_ZEROED]
163839-131072+1=32768, 对应上文的Blocks per group

  Block bitmap at 1032 (bg #0 + 1032), csum 0xf68bc9f4
  Inode bitmap at 1048 (bg #0 + 1048), csum 0x00000000
  Inode table at 3108-3619 (bg #0 + 3108)
  对应上问的Inode blocks per group(512)

  对应Inode Per Group(8192)
  0 free blocks, 8192 free inodes, 0 directories, 8192 unused inodes

  Free blocks:
  Free inodes: 32769-40960

分析INODE结构

 关于小端序:

All fields in ext4 are written to disk in little endian order. 

HOWEVER, all fields in jbd2 (the journal) are written to disk in big-endian order.

Journal模块与磁盘故障修复有关,此处暂不做深入讨论。总之我们在后续部分解读INODE结构时会用到小端序的知识。

(需使用debugfs,参考:https://blog.csdn.net/xingkong_678/article/details/40687209)
debugfs:  stat ./1.txt

————输出结果————
Inode: 398584   Type: regular    Mode:  0664   Flags: 0x80000
0x80000	Inode uses extents (EXT4_EXTENTS_FL),EXT4中的Inode结点采用Ext树方法来存储一个文件所占用的物理块的序号(一个文件的内容被有序地存于块中,但这些块在Group中的位置并不一定连续,前者被称为“逻辑块”、后者被称为“物理块”)。

Generation: 1037448172    Version: 0x00000000:00000001
User:  1000   Group:  1000   Project:     0   Size: 7
File ACL: 0
Links: 2   Blockcount: 8
这里的Blockcount中的Block依旧是一种单位,等价于512比特

Fragment:  Address: 0    Number: 0    Size: 0
 ctime: 0x613d67c4:d42ba694 -- Sat Sep 11 19:36:52 2021
 atime: 0x61432506:74176e94 -- Thu Sep 16 04:05:42 2021
 mtime: 0x613d67c4:d42ba694 -- Sat Sep 11 19:36:52 2021
crtime: 0x613d620c:592e44c4 -- Sat Sep 11 19:12:28 2021
Size of extra inode fields: 32
Inode checksum: 0xc67f4100
EXTENTS:
(0):2670562
————输出结果————

或者下面的指令,直接显示文件所占用的物理块的序号
debugfs:  blocks ./1.txt

读取指定块号的数据

sudo dd if=/dev/sda5 bs=4096 count=1 skip=2670562
如果不慎输错了bs,该指令会输出预期外的内容,推测原因是该指令会根据给定的bs对文件系统进行区块划分并查找对应的区块。

对一个稍大文件的Inode试试手!

——输出结果——
Inode: 404013   Type: regular    Mode:  0600   Flags: 0x80000
Generation: 3563925921    Version: 0x00000000:00000001
User:  1000   Group:  1000   Project:     0   Size: 27781
File ACL: 0
Links: 1   Blockcount: 64
Fragment:  Address: 0    Number: 0    Size: 0
 ctime: 0x6142ec58:ac656cf4 -- Thu Sep 16 00:03:52 2021
 atime: 0x6143f98d:553064f8 -- Thu Sep 16 19:12:29 2021
 mtime: 0x613d9245:44243738 -- Sat Sep 11 22:38:13 2021
crtime: 0x5f7b2021:4a0c5a08 -- Mon Oct  5 06:31:13 2020
Size of extra inode fields: 32
Inode checksum: 0xcaf276a7
EXTENTS:
(ETB0):2111400, (0):1626117, (1):1618417, (2):1618715, (3):1618548, (4):2107063, (5):2105480, (6):2109895
这里的ETB0是Extent树用来维护逻辑块、物理块关系的数据块。
(0,1,2....)可以视作逻辑块序号,冒号后的数字1626117,1618417,...可视作物理块序号。
——输出结果——

对一个更大的文件(~900MB)试手 

Inode: 12   Type: regular    Mode:  0600   Flags: 0x80000
Generation: 2105891747    Version: 0x00000000:00000001
User:     0   Group:     0   Project:     0   Size: 968110080
File ACL: 0
Links: 1   Blockcount: 1890848
Fragment:  Address: 0    Number: 0    Size: 0
 ctime: 0x5f7b109c:a60a2c64 -- Mon Oct  5 05:25:00 2020
 atime: 0x6142ebda:b44661b8 -- Thu Sep 16 00:01:46 2021
 mtime: 0x5f7b109c:a60a2c64 -- Mon Oct  5 05:25:00 2020
crtime: 0x5f7b811b:2df0e80c -- Mon Oct  5 13:24:59 2020
Size of extra inode fields: 32
Inode checksum: 0x6e263862
EXTENTS:
(ETB0):33796, (0-32767):34816-67583, (32768-63487):67584-98303, (63488-96255):100352-133119, (96256-126975):133120-163839, (126976-159743):165888-198655, (159744-190463):198656-229375, (190464-223231):231424-264191, (223232-236354):264192-277314
不同于教科书上的一级索引结点、二级索引结点,EXT4使用了另一种方法来记录所用的Blocks。
不难注意到,括号内的区间连起来对应0-236354,一个Block有8个512比特块,236354*8≈1890848,也就是Blockcount
如此多的Block被分段存储在数个Block连段中,对应各个没加括号的区间。

Extents are arranged as a tree. Each node of the tree begins with a struct ext4_extent_header.

Extent树中一个结点占据一个Block,结点头结构(extent_header)会指明其为叶子结点或非叶子结点。

If the node is an interior node (eh.eh_depth > 0), the header is followed by eh.eh_entries instances of struct ext4_extent_idx; each of these index entries points to a block containing more nodes in the extent tree.

对于非叶子结点,其会包含若干类型为struct ext4_extent_idx的eh_entries,指向下一层的各个结点。

If the node is a leaf node (eh.eh_depth == 0), then the header is followed by eh.eh_entries instances of struct ext4_extent; these instances point to the file's data blocks.

对于叶子结点,其会包含若干类型为struct ext4_extent的eh_entries,指向具体的文件块段(例如上文中的34816-67584,67584-98303,...)。

The root node of the extent tree is stored in inode.i_block, which allows for the first four extents to be recorded without the use of extra metadata blocks.

文件占用块数较少的场合,会直接在inode.i_block中存储Extent Nodes,节省了空间。

接下来我们配合Extent树的结构图(地址)和ETB0的十六进制码来察看Extent Node的结构: 

eh_depth(紫色):结点层数,若该值为零,则说明该结点为叶子结点。

ee_block(红色):该区间(extent)对应的第一个文件块号

ee_len(绿色):该区间包含的块数。文档中指出若该值>32768,则说明该区间没有初始化(有待进一步研究)。

因为区间包含的块是连续的,所以这里只需要记录首块的块号:
ee_start_hi(蓝色):实际块号的高16位

ee_start_lo(黑色):实际块号的低32位(对应上文输出结果中的34816,67584,100352)

分析文件夹结构

——输出结果——
Inode: 414868   Type: directory    Mode:  0775   Flags: 0x80000
Generation: 1424482529    Version: 0x00000000:00000030
User:  1000   Group:  1000   Project:     0   Size: 4096
File ACL: 0
Links: 2   Blockcount: 8
Fragment:  Address: 0    Number: 0    Size: 0
 ctime: 0x6143e6b3:16f62484 -- Thu Sep 16 17:52:03 2021
 atime: 0x6143f20c:9b797f04 -- Thu Sep 16 18:40:28 2021
 mtime: 0x6143e6b3:16f62484 -- Thu Sep 16 17:52:03 2021
crtime: 0x604cb298:c1345480 -- Sat Mar 13 04:39:52 2021
Size of extra inode fields: 32
Inode checksum: 0x89eda6de
EXTENTS:
(0):1584381
——输出结果——

使用以下指令来查看Block中的内容:
sudo dd if=/dev/sda5 bs=4096 count=1 skip=1584381 | hexdump -C

在Linux,文件夹的本质是文件名至Inode结点的映射:

Directory is more or less a flat file that maps an arbitrary byte string (usually ASCII) to an inode number on the filesystem.

其次需要注意到一点:Ext4文件系统中的文件夹结构分为两种:(1)Linear (Classic) Directories, (2) HashTreeDirectories。如果是后者,文件夹对应的Inode会被添加一个值为0x1000的flag。

接下来,结合引用资料,我们分析158438这个Block的内容。上文中Flags值为0x80000,说明这是第一类文件夹结构。

inode(红色):inode,该文件对应的inode编号

rec_len(绿色):该文件结构体的长度(例如描述第一个文件占据共12字节,这里就是0c 00)。

name_len(蓝色):文件名长度

file_type(黄色):文件类型

???(粉色):不明内容。其前面的部分对应文件名"1.txt",由于指明文件名长度为5,粉色部分不会影响文件名,同时因为rec_len为16,粉色部分是"1.txt"对应文件结构体的一部分。个人推测,该部分内容仅仅是没有被赋值为零的无用内容。

待补充的内容

  • 删除某个文件后,其父文件夹发生的变化
  • HashTreeDirectories的结构分析
  • 日志系统结构分析
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值