温馨提示×

linux readdir为何慢

小樊
36
2026-09-11 14:10:00
栏目: 智能运维

在 Linux 下,readdir(以及底层的 getdents / getdents64 系统调用)“慢”通常不是函数本身慢,而是由目录结构、文件系统、内核实现和使用方式共同决定的。下面按常见原因系统梳理一下。


一、目录本身太大(最常见)

现象

  • 一个目录下有 几十万 / 上百万 个文件
  • ls 卡住、readdir 循环很慢

原因

  • readdir 并不是一次性返回整个目录
  • 内核需要:
    1. 从磁盘 / 缓存读取目录项
    2. 逐个填充 struct dirent
  • 目录项越多,遍历时间线性增长

本质

目录不是“索引结构”,而是“线性表”(传统 ext4 / xfs 也类似)


二、目录未缓存(cache 未命中)

关键概念

  • readdir 依赖:
    • dentry cache(目录项缓存)
    • page cache(目录内容缓存)

慢的场景

  • 刚启动
  • 目录很久没访问
  • 内存紧张,cache 被回收

此时:

磁盘 I/O → 读目录块 → 填充 dirent

验证方式

free -h
cat /proc/slabinfo | grep dentry

三、文件系统类型影响巨大

不同文件系统表现差异明显

文件系统 readdir 性能
ext4 一般
xfs 较好
btrfs 可能较慢
overlayfs 明显慢(容器常见)
NFS 非常慢

特别是:

  • NFS / 网络文件系统
    • readdir 要跨网络
    • 延迟被放大
  • overlayfs
    • 需要合并多个层
    • 每个文件都要查多层

四、stat 配合 readdir(致命组合)

错误写法(非常慢)

while ((entry = readdir(dir))) {
    stat(entry->d_name, &st); // ❌
}

原因

  • readdir 只给文件名
  • stat 要:
    • 查 inode
    • 可能触发磁盘 I/O
  • 对大目录 = N 次随机 I/O

改进

  • readdir + d_type(如果支持)
  • 或者用 scandirgetdents 自己解析

五、readdir 是“流式”的,不是随机访问

特点

  • 不能 seek
  • 不能并行
  • 只能从头读到尾

如果你:

  • 想“找某个文件”
  • 却遍历整个目录

那自然慢。


六、锁与并发问题

可能慢的场景

  • 目录正在被大量写入
  • 其他进程在 create / unlink
  • 文件系统加锁(如 NFS)

此时:

  • readdir 可能被阻塞
  • 或返回不一致视图(影响性能)

七、你用的“readdir”版本不同

glibc 的 readdir 问题

  • readdir线程不安全
  • readdir_r 已废弃
  • readdir 内部有锁

高并发程序中:

  • 多线程同时 readdir
  • 锁竞争导致“看起来慢”

八、如何判断是否真的慢?

用户态测试

time ls -1 /huge_dir | wc -l

系统调用级

strace -T ls /huge_dir

关注:

getdents64(...) = ... <0.123456>

九、优化与替代方案

1. 减少目录文件数

  • 分目录(a/, b/
  • 哈希分片

2. 用 getdents64 直接读

  • 减少 glibc 封装开销

3. 避免 stat

  • 能用 d_type 就用

4. 使用合适文件系统

  • 大目录:XFS 优于 ext4

5. 使用数据库 / 索引

  • 文件多到一定程度,文件系统本来就不适合

一句话总结

Linux 的 readdir 慢,通常不是函数慢,而是“目录太大 + cache 未命中 + stat 滥用 + 文件系统特性”共同造成的。

如果你愿意,可以告诉我:

  • 用的什么文件系统
  • 目录大概多少文件
  • 是 C / Python / shell
  • 是否容器 / NFS

我可以给你针对性的优化方案或代码示例

0