在 Linux 中,readdir
是用于遍历目录内容的常用函数,基本用法如下:
#include <dirent.h>
#include <stdio.h>
void traverse_directory(const char *path) {
DIR *dir = opendir(path);
if (!dir) {
perror("opendir");
return;
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
// 过滤 . 和 ..
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
printf("%s\n", entry->d_name);
}
closedir(dir);
}
在多线程环境中,应使用 readdir_r
替代 readdir
以避免竞争条件:
struct dirent entry, *result;
while (readdir_r(dir, &entry, &result) == 0 && result != NULL) {
// 处理 entry
}
注意:在较新的 glibc 版本中(>= 2.24),readdir_r
已被弃用,因为 readdir
本身已通过内部锁实现了线程安全。
getdents
系统调用比 readdir
更底层,可以一次性获取更多目录项:
#include <sys/syscall.h>
#include <linux/dirent.h>
#define BUF_SIZE 1024*1024
void fast_traverse(const char *path) {
int fd = open(path, O_RDONLY | O_DIRECTORY);
if (fd < 0) {
perror("open");
return;
}
char buf[BUF_SIZE];
while (1) {
int nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
if (nread == -1) {
perror("getdents");
break;
}
if (nread == 0) break;
for (int bpos = 0; bpos < nread;) {
struct linux_dirent *d = (struct linux_dirent *)(buf + bpos);
char d_type = *(buf + bpos + d->d_reclen - 1);
// 处理目录项 d->d_name
bpos += d->d_reclen;
}
}
close(fd);
}
利用 dirent
结构中的 d_type
字段(如果支持),避免不必要的 stat
调用:
if (entry->d_type == DT_DIR) {
// 是目录,递归处理
} else if (entry->d_type == DT_REG) {
// 是普通文件
}
O_DIRECTORY | O_NOATIME
打开目录posix_fadvise
预读提示#include <stdio.h>
#include <dirent.h>
#include <queue>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool done = false;
void worker(std::queue<std::string>& dir_queue) {
while (true) {
std::string dir_path;
{
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [&]{ return !dir_queue.empty() || done; });
if (done && dir_queue.empty()) return;
dir_path = dir_queue.front();
dir_queue.pop();
}
DIR *dir = opendir(dir_path.c_str());
if (!dir) continue;
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_name[0] == '.') continue;
std::string full_path = dir_path + "/" + entry->d_name;
if (entry->d_type == DT_DIR) {
std::lock_guard<std::mutex> lock(mtx);
dir_queue.push(full_path);
cv.notify_one();
} else {
// 处理文件
printf("%s\n", full_path.c_str());
}
}
closedir(dir);
}
}
void parallel_traverse(const char *root, int num_threads) {
std::queue<std::string> dir_queue;
dir_queue.push(root);
std::vector<std::thread> threads;
for (int i = 0; i < num_threads; ++i) {
threads.emplace_back(worker, std::ref(dir_queue));
}
{
std::lock_guard<std::mutex> lock(mtx);
done = true;
cv.notify_all();
}
for (auto& t : threads) {
t.join();
}
}
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
基本 readdir | 简单易用 | 性能一般 | 简单目录遍历 |
getdents | 高性能 | 实现复杂 | 需要极致性能 |
多线程 | 利用多核 | 同步复杂 | 深层目录结构 |
广度优先 | 内存友好 | 实现稍复杂 | 超大目录 |
选择优化方案时应根据具体场景权衡实现复杂度和性能收益。