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"