什么是文件碎片?
文件碎片指文件内容分散存储在存储设备中,无法做到物理连续。如下图,文件的3个扩展分布在3个组,文件内容在bdev上存储没有做到物理连续。

为什么会产生碎片?
产生碎片的原因不唯一,比如存在空洞,追加写等等。这里以追加写为例来简要说明。开始,文件只有一个extent 0,物理位置在Group 1,内容物理连续不存在碎片。

随着文件追加写,Group 1内已无可用空间,因此文件追加内容存储在有可用空间的Group 0。于是,文件内容对应2个extent,未做到物理连续,碎片产生。

为什么需要整理?
在回答该问题前,先说明一下什么是整理。碎片整理是将文件分散存储的内容尽量整合到一块物理连续的存储空间,经过整合后,文件的extent数量减少。
那么,为什么需要整理?通常使用较极端的例子比较容易理解,假设存储设备内的各个文件存在大量碎片,这至少会产生两方面的影响。
1)文件顺序读取因内容分布不连续而被动变为随机读
2)删除文件后,因碎片化而释放的空间物理不连续,这些可用空间在用于其他文件写入过程中,会导致顺序写被动变为随机写
顺序读写被动变为随机读写,导致读写性能下降,因此需要进行碎片整理。
e4defrag工作原理
e4defrag是ext4文件系统的在线文件碎片整理工具,支持整理的对象包括:1)目录文件2)常规文件3)设备文件。
需要说明的是,e4defrag进行碎片整理的单位是常规文件。以目录文件为整理对象时,实际是遍历目录下的常规文件进行整理。以设备文件为整理对象时,实际是遍历设备内的全部常规文件进行整理,所以e4defrag的工作原理即为对常规文件进行整理的原理。
常规文件碎片整理简要步骤如下,
获取文件extent信息
使用ioctl(FS_IOC_FIEMAP)遍历获取文件全部extent并以物理地址由小到大排序加入orig_list_physical链表,遍历orig_list_physical链表,依次以逻辑地址由小到大排序加入orig_list_logical链表,链表元素个数即为整理前的碎片数。如下图所示,文件在碎片整理前存在4个extent,由于文件内部存在空洞,因此extent1和extent2的逻辑地址不连续。那么,理想状态下,经过碎片整理后的文件extent数量由4个降低为2个,但是整理后的两个extent同样因为存在hole而逻辑不连续,因此这两个extent也不能简单设置为物理地址连续,这样如果文件hole如被填充,因为无法进行extent合并而产生新的碎片。

经过逻辑地址排序后的链表内的元素还需要以是否逻辑地址连续进行分组,一组内的extent彼此间逻辑地址连续。经过上述处理,文件extent的数据组织如下图所示,extent对应fiemap_extent_data对象,以fiemap_extent_list组成链表,链表内逻辑地址前后连续的fiemap_extent_list成员组合成为一个group,由fiemap_extent_group管理。

创建donor file
创建donor file为文件碎片整理申请空间,文件逻辑地址连续同时物理地址连续是最佳状态。因此,以最佳状态为目标,以fiemap_extent_group为单位,以每组内extent的总块数作为donor file一次申请的连续区域大小,有几个组就申请几块连续区域。因为可用空间可能本身比较分散,无法提供大段的连续空间,所以申请的空间是否真的物理连续,还需要获取donor file实际的extent信息。若donor file的extent数量比我们要进行碎片整理文件的extent数量还多,无法达到整理目的,会中止对该文件的整理。
如果donor file获得的存储空间物理更连续,extent的数量更少,则将开始真正的碎片整理。
进行extent交换
不难理解,整理过程是将被整理文件与donor文件的extent进行交换,以page为单位,一次交换一个page对应的物理块。
下图可辅助理解之前的操作。待碎片整理文件有extent 0和extent 1两个extent,物理内容分别存储于Group 1和Group 0。两个extent逻辑地址连续,因此仅存在一个fiemap_extent_group。
donor文件以fiemap_extent_group组内extent(extent 0和extent 1)的物理块总数作为申请连续空间的大小,得到donor文件的extent 0,位于Group 3。
将碎片文件的extent与donor文件的extent进行交换以完成碎片整理。交换后,文件的extent数量为1,文件内容做到了物理存储连续。

文件碎片整理FAQ
1、已被其他进程打开的碎片文件是否可进行碎片整理?
会进行。
2、正在读写的碎片文件是否可进行碎片整理?
会进行。
3、文件在碎片整理过程中被截断会怎么样?
整理过程中通过inode_lock锁定禁止截断,所以在整理完成后才能截断文件,要进行截断操作的进程会被阻塞。
4、文件在碎片整理过程追加写会怎么样?
不受影响,追加写对应新增extent,碎片整理的是已有extent,因此追加写操作不会被阻塞。
5、文件在碎片整理过程覆盖写会怎么样?
整理以page为单位,在整理过程会lock_page,如果覆盖写正好要写的是正在整理过程中的page,那么执行覆盖写的进程会阻塞,等待该page对应的物理块整理完成,其他情况则不被阻塞。
6、读取正在碎片整理的文件会怎么样?
不受影响。
7、文件碎片整理过程中异常掉电会怎么样?
文件碎片可能会仍然存在,文件内容不受影响。
8、文件碎片整理有什么副作用?
整理文件本质是读取文件内容再重新写入,在整理过程势必占用io带宽,额外的写入操作对存储设备会产生对应的磨损,并可能短暂阻塞同时操作该文件的进程。