在 Linux 下使用 readdir(以及相关的目录遍历接口)时,性能瓶颈通常出现在 系统调用次数、缓存命中率、文件系统类型、并发能力 等方面。下面从原理到实践系统讲一下如何提升 readdir 性能。
Linux 中常见的目录读取方式:
| 接口 | 说明 |
|---|---|
opendir / readdir |
glibc 封装,最常用 |
readdir_r |
线程安全版本(已不推荐) |
getdents / getdents64 |
底层系统调用 |
scandir |
一次性读入并排序 |
fts / nftw |
高级遍历库 |
性能优化核心:减少系统调用 + 利用内核缓存 + 减少用户态开销
getdents64 而不是 readdirreaddir 每次只返回一个目录项,而 getdents64 一次读取 多个:
#include <sys/syscall.h>
#include <unistd.h>
#include <linux/dirent.h>
syscall(SYS_getdents64, fd, buf, size);
✅ 优点:
✅ 适合:
readdir 内部 buffer 通常不大,可以:
DIR *dir = opendir(path);
// 默认 buffer 较小
改进方式:
fdopendir + 手动 getdentschar buf[128 * 1024];
stat 每个文件(关键!)很多人写:
while ((de = readdir(dir))) {
stat(de->d_name, &st); // ❌ 极慢
}
✅ 优化:
readdir,不 statd_type(如果支持)if (de->d_type == DT_REG) {
// 普通文件
}
⚠️ 注意:
d_type 在 ext4 / xfs 支持如果目录里有 百万级文件,再怎么优化都慢。
✅ 优化方案:
/data/ab/cd/ef/xxx
✅ 原则:
✅ 预热缓存:
ls -R /path >/dev/null
或第一次遍历后,后续会明显变快。
scandir(合适场景)如果你需要:
struct dirent **namelist;
int n = scandir(path, &namelist, NULL, alphasort);
⚠️ 注意:
✅ 适合:
thread1 -> /a
thread2 -> /b
⚠️ 不要多线程读同一个目录(无收益)
Linux 5.1+ 支持:
getdentsstat✅ 极致性能方案
❌ 实现复杂
| 文件系统 | readdir 性能 |
|---|---|
| ext4 | 好 |
| xfs | 非常好 |
| btrfs | 一般 |
| NFS | 差(网络 + 无缓存) |
| overlayfs | 较差 |
✅ 大目录场景优先:
dir_index)| 场景 | 方法 | 相对速度 |
|---|---|---|
| 10w 文件 | readdir + stat | 1x |
| 10w 文件 | getdents64 | 5–10x |
| 100w 文件 | 拆分目录 | 10x+ |
| NFS | 本地缓存 | 不确定 |
readdir 性能优化 = 减少 syscall + 避免 stat + 大 buffer + 好目录结构 + 合适文件系统
如果你愿意,可以告诉我:
stat我可以给你一段 针对性优化代码。