getdents64 函数详解
1. 函数介绍
getdents64
是 Linux 系统中用于读取目录内容的底层系统调用。可以把这个函数想象成一个"目录内容扫描仪"——它能够高效地扫描目录中的所有文件和子目录,就像超市的扫描枪快速读取商品条码一样。
与高级的目录操作函数(如 readdir
)不同,getdents64
是最底层的接口,直接与内核交互,提供了最大的灵活性和性能。它返回的是原始的目录项数据,包含文件名、inode 号、文件类型等信息。
2. 函数原型
#include <dirent.h> /* 或者 <unistd.h> */
#include <sys/syscall.h>int getdents64(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count);
3. 功能
getdents64
函数用于从已打开的目录文件描述符中读取目录项(directory entries)。它一次可以读取多个目录项,比逐个读取效率更高。
4. 参数
- fd: 已打开的目录文件描述符(通过
open()
或opendir()
获得) - dirp: 指向缓冲区的指针,用于存储读取的目录项数据
- count: 缓冲区的大小(以字节为单位)
5. struct linux_dirent64 结构体
struct linux_dirent64 {ino64_t d_ino; /* 64位 inode 号 */off64_t d_off; /* 到下一个目录项的偏移 */unsigned short d_reclen; /* 此目录项的长度 */unsigned char d_type; /* 文件类型 */char d_name[]; /* 文件名(以 null 结尾) */
};
6. 文件类型(d_type 字段)
类型值 | 宏定义 | 说明 |
---|---|---|
0 | DT_UNKNOWN | 未知类型 |
1 | DT_FIFO | 命名管道 |
2 | DT_CHR | 字符设备 |
4 | DT_DIR | 目录 |
6 | DT_BLK | 块设备 |
8 | DT_REG | 普通文件 |
10 | DT_LNK | 符号链接 |
12 | DT_SOCK | 套接字 |
7. 返回值
- 成功: 返回实际读取的字节数(0 表示到达目录末尾)
- 失败: 返回 -1,并设置相应的 errno 错误码
常见错误码:
EBADF
: fd 不是有效的目录文件描述符EFAULT
: dirp 指针无效EINVAL
: 参数无效ENOENT
: 目录不存在
8. 相似函数或关联函数
- getdents: 旧版本的目录读取函数(32位 inode)
- readdir: POSIX 标准的目录读取函数(更高级的接口)
- opendir/fdopendir: 打开目录
- closedir: 关闭目录
- scandir: 扫描目录并排序
- ls: 命令行目录列表工具
9. 示例代码
示例1:基础用法 - 读取目录内容
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <dirent.h>
#include <string.h>// 目录项结构体(64位版本)
struct linux_dirent64 {ino64_t d_ino; /* Inode number */off64_t d_off; /* Offset to next structure */unsigned short d_reclen; /* Size of this structure */unsigned char d_type; /* File type */char d_name[]; /* Filename (null-terminated) */
};// 获取文件类型字符串
const char* get_file_type_string(unsigned char d_type) {switch (d_type) {case DT_REG: return "普通文件";case DT_DIR: return "目录";case DT_LNK: return "符号链接";case DT_CHR: return "字符设备";case DT_BLK: return "块设备";case DT_FIFO: return "命名管道";case DT_SOCK: return "套接字";case DT_UNKNOWN: default: return "未知";}
}// 获取文件类型字符
char get_file_type_char(unsigned char d_type) {switch (d_type) {case DT_REG: return 'f';case DT_DIR: return 'd';case DT_LNK: return 'l';case DT_CHR: return 'c';case DT_BLK: return 'b';case DT_FIFO: return 'p';case DT_SOCK: return 's';case DT_UNKNOWN: default: return '?';}
}int main(int argc, char *argv[]) {int fd;char buf[4096];int nread;char *dir_path;// 获取目录路径参数if (argc != 2) {printf("用法: %s <目录路径>\n", argv[0]);dir_path = "."; // 默认当前目录printf("使用当前目录: %s\n\n", dir_path);} else {dir_path = argv[1];}// 打开目录fd = open(dir_path, O_RDONLY | O_DIRECTORY);if (fd == -1) {perror("open");return 1;}printf("=== 目录 '%s' 的内容 ===\n", dir_path);printf("%-12s %-10s %-8s %s\n", "INODE", "类型", "大小", "名称");printf("%-12s %-10s %-8s %s\n"