《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #19 ext4的调整

时间:2022-02-02 23:32:58

HACK #19 ext4的调整

本节介绍可以从用户空间执行的ext4调整。
ext4在sysfs中有一些关于调整的特殊文件(见表3-6)。使用这些特殊文件,就不用进行内核编译、重启,直接从用户空间确认、更改内核空间的设置参数。
表3-6 sysfs中的ext4文件
《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #19 ext4的调整
《Linux内核精髓:精通Linux内核必会的75个绝技》一HACK #19 ext4的调整

/sys/fs/ext4/<设备名>下有与文件系统相关的各种文件。表3-6所示为这些文件的说明和默认值的列表。
这里将具体介绍上述文件中最为有效的部分文件。

  1. lifetime_write_kbytes与session_write_kbytes
    lifetime_write_kbytes虽然不是可调整的入口,但是在SSD上生成ext4的情况下有时非常有用。SSD对写入次数有限制。通过磨损平衡(wear leveling)将写入分散化,从而延长了寿命,但掌握目前为止写入文件系统的数据量对于预测SSD寿命也是非常有用的信息。

lifetime_write_kbytes以千字节为单位记录写入文件系统的数据量,session_write_kbytes以千字节为单位记录文件系统挂载后写入的数据量。
下例中展现的就是lifetime_write_kbytes和session_write_kbytes的值。
即使在刚生成ext4后,lifetime_write_kbytes也会显示已有数据写入。这表示执行mkfs时写入的元数据量。而sesson_write_kbytes中记录的是挂载文件系统的处理中产生的数据被写入的信息。

# cat /sys/fs/ext4/sda5/lifetime_write_kbytes
10637

# cat /sys/fs/ext4/sda5/session_write_kbytes
1

在这个ext4上生成100MB的文件。可以看到值分别增加了约100MB。由于元数据的写出也会计数,因此是约100MB。

# dd if=/dev/urandom of=/mnt/mp1/file bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 18.322 s, 5.7 MB/s

# cat /sys/fs/ext4/sda5/lifetime_write_kbytes
113099

# cat /sys/fs/ext4/sda5/session_write_kbytes
102463

再次挂载文件系统后,lifetime_write_kbytes的值会有一些增加。这是因为文件系统的挂载处理中有更新的数据(例如,内存上的超级块信息等)写入磁盘。而session_write_kbyte重置为0。

# umount /mnt/mp1
# mount -t ext4 /dev/sda5 /mnt/mp1

# cat /sys/fs/ext4/sda5/lifetime_write_kbytes
113105
# cat /sys/fs/ext4/sda5/session_write_kbytes
1
2. mb_stream_req

ext4中安装了多块分配处理功能,可以降低块分配处理中的CPU成本。可以将多个块分配到物理上连续的区域,提高I/O的效率。
另外,ext4可以使用fallocate系统调用进行块的持久预分配,而多块分配处理中安装的则是内核内部使用的group预分配(下称group PA)和inode预分配(下称inode PA)。
group PA由各CPU分别管理,用来将对象文件块(满足某条件的块)排列到物理上较近的区域,inode PA用来将某一个文件的块排列到物理上连续的区域。
这些预分配算法的切换是如何进行的呢?ext4.h里的下列定义就是其阈值。

#define MB_DEFAULT_STREAM_THRESHOLD    16 /* 64k */

由于是以块数为单位,因此块的大小4KB时阈值为64KB,块的大小为1KB时阈值为16KB。
默认设置表示对于16块以下的块分配处理使用group PA,对于16块以上则使用inode PA。这个值可以使用mb_stream_req来更改。
默认值设置为16块,是因为设置文件等多数文件都在16块(块的大小4KB时阈值为64KB)以内。因此,这些文件通过group PA实现物理上的局部化。在系统启动时等数据读入处理操作中,通过将文件数据局部化,可以缩短磁盘的寻道时间(seek time)。
实际操作mb_stream_req,来验证一下group PA和inode PA的运行和效果。为了便于测定预分配的效果,这次操作中将延迟分配禁用。

# mount -t ext4 -o nodelalloc /dev/sdb5 /mnt/mp1

# cat /proc/mounts | grep sdb5
/dev/sdb5 /mnt/mp1 ext4
rw,relatime,user_xattr,barrier=1,nodelalloc,data=ordered 0 0

为了使用group PA,将mb_stream_req设置为64,并向文件分配小于这个值的32块。

# echo 64 > /sys/fs/ext4/sdb5/mb_stream_req

# for i in 1 2 3; do dd if=/dev/urandom of=/mnt/mp1/file$i bs=4K count=32 >
/dev/null 2>&1; done

# ls -l /mnt/mp1/file*
-rw-r--r-- 1 root root 135168 2011-05-17 01:10 /mnt/mp1/file1
-rw-r--r-- 1 root root 135168 2011-05-17 01:10 /mnt/mp1/file2
-rw-r--r-- 1 root root 135168 2011-05-17 01:10 /mnt/mp1/file3

可以使用e2fsprogsa工具包的fileflag命令来确认分配给磁盘的数据块。根据physical和length显示的值,可以看出通过group PA,把各个文件的数据块排列在物理上相近的位置。

# for i in 1 2 3; do filefrag -v /mnt/mp1/file$i; done

Filesystem type is: ef53
File size of /mnt/mp1/file1 is 131072 (32 blocks, blocksize 4096)
ext logical physical expected length flags
0 0 33280 32 eof
/mnt/mp1/file1: 1 extent found
Filesystem type is: ef53
File size of /mnt/mp1/file2 is 131072 (32 blocks, blocksize 4096)
ext logical physical expected length flags
0 0 33792 32 eof
/mnt/mp1/file2: 1 extent found
Filesystem type is: ef53
File size of /mnt/mp1/file3 is 131072 (32 blocks, blocksize 4096)
ext logical physical expected length flags
0 0 33312 32 eof

然后,为了确认inode PA的运行,再将mb_stream_req设置为16,并向文件分配大于这个值的32块。

# rm -rf /mnt/mp1/file*

# umount /mnt/mp1

# mount -t ext4 -o nodelalloc /dev/sdb5 /mnt/mp1

# echo 16 > /sys/fs/ext4/sdb5/mb_stream_req

# for i in 1 2 3; do dd if=/dev/urandom of=/mnt/mp1/file$i bs=4K count=32 >
/dev/null 2>&1; done

# for i in 1 2 3; do filefrag -v /mnt/mp1/file$i; done

Filesystem type is: ef53
File size of /mnt/mp1/file1 is 131072 (32 blocks, blocksize 4096)
ext logical physical expected length flags
0 0 33280 16
1 16 33072 33295 16 eof
/mnt/mp1/file1: 2 extents found
Filesystem type is: ef53
File size of /mnt/mp1/file2 is 131072 (32 blocks, blocksize 4096)
ext logical physical expected length flags
0 0 33296 16
1 16 33088 33311 16 eof
/mnt/mp1/file2: 2 extents found
Filesystem type is: ef53
File size of /mnt/mp1/file3 is 131072 (32 blocks, blocksize 4096)
ext logical physical expected length flags
0 0 33792 16
1 16 33104 33807 16 eof
/mnt/mp1/file3: 2 extents found

在多于mb_stream_request的块分配处理中,将使用inode PA。inode PA只在引用对象文件期间保留有效的预分配块。从而在按顺序向对象文件写入时,把块分配到物理上连续的区域,抑制碎片的产生。要注意的是,inode PA在块分配中并不是每次都会生成,而是只在多块分配处理中获取的连续空闲块与要求的块数之间有差异时使用。例如,对于16块的分配要求,当多块分配处理检索到连续空闲块有16个时,就不会生成inode PA。
在上述块分配中不生成inode PA,file1的第一个范围(extent)是从物理偏移量33 280分配16块,而从其后的物理偏移量33 296分配的是file2的第一个范围的块。这样就会产生碎片注5。
为了避免产生碎片,文件系统上就必须有足够的连续空闲块。使用e2fsprogs工具包中包含的e2freefrag命令来确认有多少连续的空闲块。在下例中,Extent Size Range越大,在整体中所占的比例越大,可见这个文件系统上有充足的连续空闲块。

# e2freefrag /dev/sdb5

Device: /dev/sdb5
Blocksize: 4096 bytes
Total blocks: 1220703
Free blocks: 1166377 (95.5%)

Min. free extent: 40 KB
Max. free extent: 917504 KB
Avg. free extent: 358884 KB
Num. free extent: 13

HISTOGRAM OF FREE EXTENT SIZES:
Extent Size Range : Free extents Free Blocks Percent
32K... 64K- : 2 20 0.00%
64M... 128M- : 2 49102 4.21%
128M... 256M- : 5 326180 27.97%
512M... 1024M- : 4 791075 67.82%

产生的碎片可以使用e4defrag命令来消除。发布版(例如Fedora 14)的e2fsprogs工具包中还未收入e4defrag命令。在使用时就需要获取由Git管理的e2fsprogs工具包。使用git命令获取e2fsprogs的方法请参考HACK #17注6。
下面是执行e4defrag命令的示例。可以看出原来有17个的文件碎片已消除。

# e4defrag -v  /mnt/mp1/file1
ext4 defragmentation for /mnt/mp1/file1
[1/1]/mnt/mp3/file1: 100% extents: 17 -> 1 [ OK ]
Success: [1/1]

上文介绍了更改mb_stream_request的值时块的分配的差异。通过切换group PA、inode PA,就可以在文件系统上对文件进行布局。有时还可以提高顺序读入中的I/O性能。

  1. inode_readahead_blks
    使用inode_readahead_blks可以控制进行预读的inode表的数量。inode表是指磁盘上储存inode结构的区域,被分配给各块组。与下列flex_bg标志同时使用,就可以提高运行效率。

下例输出的是启用/禁用功能标志flex_bg时inode表的位置。
启用flex_bg

# mke2fs -t ext4 /dev/sda4; dumpe2fs /dev/sda4 | egrep "Group|Inode table"
dumpe2fs 1.41.14 (22-Dec-2010)
Group 0: (Blocks 0-32767) [ITABLE_ZEROED]
Primary superblock at 0, Group descriptors at 1-1
Inode table at 332-833 (+332)
Group 1: (Blocks 32768-65535) [INODE_UNINIT, ITABLE_ZEROED]
Backup superblock at 32768, Group descriptors at 32769-32769
Inode table at 834-1335 (bg #0 + 834)
Group 2: (Blocks 65536-98303) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED]
Inode table at 1336-1837 (bg #0 + 1336)
Group 3: (Blocks 98304-131071) [INODE_UNINIT, ITABLE_ZEROED]
Backup superblock at 98304, Group descriptors at 98305-98305
Inode table at 1838-2339 (bg #0 + 1838)
...

禁用flex_bg

# mke2fs -t ext4 -O ^flex_bg /dev/sda4; dumpe2fs /dev/sda4 | egrep "Group|Inode table"
dumpe2fs 1.41.14 (22-Dec-2010)
Group 0: (Blocks 0-32767) [ITABLE_ZEROED]
Primary superblock at 0, Group descriptors at 1-1
Inode table at 302-803 (+302)
Group 1: (Blocks 32768-65535) [INODE_UNINIT, ITABLE_ZEROED]
Backup superblock at 32768, Group descriptors at 32769-32769
Inode table at 33070-33571 (+302)
Group 2: (Blocks 65536-98303) [INODE_UNINIT, BLOCK_UNINIT, ITABLE_ZEROED]
Inode table at 65538-66039 (+2)
Group 3: (Blocks 98304-131071) [INODE_UNINIT, ITABLE_ZEROED]
Backup superblock at 98304, Group descriptors at 98305-98305
Inode table at 98606-99107 (+302)
...

启用flex_bg时,“Inode table at”显示的物理块编号连续,可以看出各块组的inode表排列在物理上连续的区域。
flex_bg在标准设置中是将每16块组的元数据集中在一处。因此,各块组的inode表按照0~15、16~31的单位集中在一处。
因此不启用flex_bg时,有时即使为inode_readahead_blks设置较大的值,inode表的区域也并不连续,因此没有什么意义。
在连续访问多个文件的处理中,可以将inode_readahead_blks设置较大的值,一次读入大量inode表,这样效率更高。
在下面这个极端的例子中,将inode_readahead_blks分别设置为1和4096时的内核源代码读入时间进行了比较。启用ext4的flex_bg。
当inode_readahead_blks为1时

# time find /mnt/mp1/linux-2.6.39-rc1 -type f -exec cat > /dev/null {} \;
real 3m36.599s
user 0m5.092s
sys 0m43.510s

当inode_readahead_blks为4096时

# time find /mnt/mp1/linux-2.6.39-rc1 -type f -exec cat > /dev/null {} \;
real 3m23.613s
user 0m5.080s
sys 0m43.341s

执行时间的差异为约6%。根据运用环境的不同也会有一些差异,在经常需要连续读取多个文件的环境下,就可以通过更改inode_readahead_blks来提高读入性能。
小结
本章介绍了使用sysfs的ext4调整。可以从用户空间对内核参数进行操作,非常方便。
如果在ext4的源代码中发现了其他可以从用户空间进行调整的项目,请尝试向邮件地址列表投稿。对于新的结构或功能会有积极的参考作用,这时如果有补丁的话一定会得到很多反馈。
参考文献
Ext4 (and Ext2/Ext3)Wiki
https://ext4.wiki.kernel.org/index.php/Main_Page
Mailing list ARChives
http://marc.info/?l=linux-ext4&r=1&w=2
—Akira Fujita