关于Linux ext4文件系统中readdir()读取顺序与目录设计的技术咨询
哥们,你的问题问到点子上了——这其实是ext4文件系统为了性能做的设计取舍,我来给你掰扯清楚:
首先得明确:你说的没错,Linux里的目录确实是文件引用的集合(也就是存储着指向文件inode的目录项dentry),不是一块用来“放文件”的空间。但ext4存储这些目录项的方式,直接决定了readdir()的返回顺序。
为什么readdir()会返回“乱序”的文件名?
ext4默认启用了dir_index特性(也就是哈希树索引,htree),这个特性的核心目的是提升大目录的文件查找效率。它会把每个文件名通过哈希函数计算出一个哈希值,然后将目录项按哈希值分组存储。当你调用readdir()时,它会按哈希树的遍历顺序返回这些目录项——这个顺序既不是你创建文件的写入顺序,也不是文件名的数字/字典顺序,完全由文件名的哈希值决定。这就是你看到38、72、101这种“无厘头”顺序的原因,本质是这些文件名的哈希值排序后的结果。
能不能按写入顺序读取,或者让写入的文件按期望顺序被读取?
我分几部分给你说:
按写入顺序读取的可行方案
- 首先,ext4本身不会把“文件创建顺序”记录到目录结构里(除非你关闭
dir_index特性)。如果关闭dir_index,目录会变成线性存储,这时候readdir()的返回顺序会大致和写入顺序一致,但有个坑:如果中间有文件被删除,新创建的文件会复用之前空出来的目录项位置,这时候顺序又会变得混乱,完全不可靠。 - 更稳定的办法是在用户空间自己处理排序:读取所有目录项之后,要么按文件名的数字值转换后排序(你的文件名都是数字,把字符串转成整数再比较就行),要么按文件的创建时间排序(Linux内核4.11及以上支持通过
statx()系统调用获取文件的真实创建时间STX_BTIME,以此为依据排序)。
- 首先,ext4本身不会把“文件创建顺序”记录到目录结构里(除非你关闭
有没有系统调用能直接控制
readdir()的返回顺序?
很遗憾,没有。POSIX标准里根本没规定readdir()的返回顺序,不同文件系统的返回顺序都不一样——比如ext2默认是线性存储顺序,ext3不开dir_index也是线性顺序,ext4默认是哈希顺序。所以不存在任何系统调用能改变readdir()的返回顺序,这完全由文件系统的实现逻辑决定。写入时能不能让文件按期望顺序被读取?
本质上做不到,因为文件系统的目录项存储位置是由它的索引机制自动管理的,你没法手动干预。除非你关闭ext4的dir_index特性(执行tune2fs -O ^dir_index /dev/your-partition,注意必须先卸载分区),但这会导致大目录的查找性能急剧下降,当目录里有几千上万个文件时,查找速度会慢得离谱,非常不推荐。
最后给你个最优建议
如果你的需求是按数字顺序处理文件,绝对不要依赖readdir()的返回顺序——哪怕现在看起来顺序对了,后续文件的增删也会让顺序混乱。最稳定可靠的方式就是:用readdir()读取所有文件名,然后在自己的代码里对这些文件名进行排序(按数字大小或者创建时间),再按排序后的顺序处理文件。
内容来源于stack exchange

