温馨提示×

linux readdir怎么避免漏读

小樊
47
2026-09-29 19:52:02
栏目: 智能运维

在 Linux 下使用 readdir()(或 readdir_r())遍历目录时,“漏读”通常不是 readdir 本身的 bug,而是使用方式或并发场景导致的问题。下面系统说明常见原因和解决办法。


一、正确使用 readdir 的基本方式(避免低级漏读)

1. 必须循环读到 NULL

DIR *dir = opendir(path);
if (!dir) { perror("opendir"); return; }

struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
    // 处理 entry
}
closedir(dir);

⚠️ 常见错误:

  • 只调用一次 readdir
  • 在 while 条件里做了额外判断导致提前 break
  • 使用 readdir_r 但没检查返回值

二、并发修改导致“漏读 / 重复读”

现象

在遍历目录时:

  • 其他进程/线程 创建、删除、重命名文件
  • 可能导致某些文件:
    • 没被读到(漏读)
    • 或被读两次(重复)

原因

readdir 基于目录的 offset(目录项偏移),不是快照。

解决方案

✅ 方案 1:遍历前“冻结”目录(推荐)

  • 先 opendir
  • 复制目录项到内存
  • 再处理
// 先读出所有文件名
std::vector<std::string> names;
DIR *dir = opendir(path);
struct dirent *e;
while ((e = readdir(dir)))
    names.push_back(e->d_name);
closedir(dir);

✅ 简单、安全
❌ 不适合超大目录


✅ 方案 2:加锁(多进程/线程)

  • 文件锁(flock)
  • 或应用层锁
flock(dirfd(dir), LOCK_SH);
// readdir
flock(dirfd(dir), LOCK_UN);

⚠️ 只防“自己程序”,不防别的进程


✅ 方案 3:使用 inotify + 扫描(高级)

  • 先 inotify 监听
  • 再扫描目录
  • 合并事件

适合“实时同步”类程序


三、readdir_r 的坑(多线程)

问题

  • readdir 不是线程安全
  • readdir_r 才是

但很多人用错:

struct dirent entry;
struct dirent *result;
readdir_r(dir, &entry, &result);

✅ 正确做法:

struct dirent *entry = malloc(sizeof(struct dirent));
struct dirent *result;
readdir_r(dir, entry, &result);

四、符号链接 & 隐藏文件导致的“误以为漏读”

易混淆情况

  • . 和 .. 被忽略
  • 符号链接未解析
  • 权限不足导致某些子目录进不去

✅ 建议:

if (entry->d_name[0] == '.') continue; // 按需

五、极端情况:文件系统类型问题

某些文件系统(网络 FS、FUSE):

  • 不支持稳定 offset
  • readdir 可能返回不完整

✅ 解决:

  • 使用 getdents64
  • 或避免在网络 FS 上强一致遍历

六、总结:避免 readdir 漏读的最佳实践

✅ 必做:

  • 循环读到 NULL
  • 不在遍历中修改目录
  • 多线程用 readdir_r

✅ 推荐:

  • 先快照再处理
  • 加锁或 inotify
  • 超大目录分批处理

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

  • 是 C / C++ / Shell?
  • 单线程还是多线程?
  • 本地还是网络文件系统?

我可以给你针对性代码示例。

0 踩