温馨提示×

linux readdir如何避免漏读

小樊
43
2026-09-14 19:13:38
栏目: 智能运维

在 Linux 下使用 readdir()(或 readdir_r())遍历目录时,“漏读”通常不是 readdir() 本身的 bug,而是使用方式或并发环境导致的问题。下面从原因 → 解决方案 → 示例代码系统说明。


一、常见导致“漏读”的原因

1. 在遍历目录时修改目录内容

  • 遍历过程中:
    • 创建文件
    • 删除文件
    • 重命名文件
  • 可能导致:
    • 某些文件被跳过
    • 某些文件被读两次

✅ 这是 POSIX 允许的行为,不是 bug


2. 使用 opendir() + readdir() 时目录被替换

  • 例如 rm -rf dir && mv newdir dir
  • DIR* 仍指向旧 inode

3. 多线程并发使用同一个 DIR*

  • readdir() 不是线程安全的
  • 多线程共享 DIR* 会互相干扰

4. 未正确处理返回值

  • 忽略 NULL
  • 未区分“结束”和“错误”

二、避免漏读的正确做法

✅ 1. 遍历期间不要修改目录

最推荐方案

  • 遍历前:
    • 停止写目录
  • 或:
    • readdir() 收集文件名
    • 关闭目录后再处理
while ((ent = readdir(dir))) {
    names.push_back(ent->d_name);
}
closedir(dir);
// 再处理 names

✅ 2. 使用 fdopendir() + 文件锁(高级)

防止别人改目录:

int fd = open(path, O_RDONLY | O_DIRECTORY);
flock(fd, LOCK_SH);
DIR *dir = fdopendir(fd);

注意:只对其他遵守锁的进程有效


✅ 3. 使用 readdir_r()(线程安全)

(已不推荐,但可用)

struct dirent entry;
struct dirent *result;

while (readdir_r(dir, &entry, &result) == 0 && result) {
    // 使用 entry.d_name
}

⚠️ glibc 2.34+ 已标记 readdir_r 为 deprecated


✅ 4. 完整正确处理返回值

errno = 0;
while ((ent = readdir(dir)) != NULL) {
    // 正常处理
}

if (errno != 0) {
    perror("readdir");
}

否则错误会被误认为“遍历结束”


✅ 5. 如果必须实时遍历:用 inotify

  • readdir() 不适合“持续监听”
  • inotify 监听创建/删除事件

三、如果你担心“文件很多 / 大目录”

  • readdir() 本身不会漏
  • 但:
    • 性能差
    • 容易被并发影响

可选替代:

  • getdents64()( syscalls,更底层)
  • 数据库 / 索引方案

四、总结(避免漏读 checklist)

✅ 不要在遍历时改目录
✅ 遍历完再处理
✅ 正确处理 NULLerrno
✅ 不要多线程共享 DIR*
✅ 必要时加锁或使用 inotify


如果你能说明:

  • 单线程还是多线程
  • 是否 边遍历边删除/创建
  • 一次性遍历还是持续监听

我可以给你更精确的代码方案。

0