getdents系统调用及示例

getdents - 获取目录项

函数介绍

getdents系统调用用于读取目录文件的内容,获取目录中的文件项信息。它返回原始的目录项结构,需要手动解析。这是一个低级别的目录读取函数。

函数原型

1
2
3
4
5
#include <dirent.h>
#include <sys/syscall.h>

int getdents(unsigned int fd, struct linux_dirent *dirp, unsigned int count);

功能

从目录文件描述符中读取目录项信息。

参数

  • unsigned int fd: 目录文件描述符(通过opendir或open获得)

  • struct linux_dirent *dirp: 指向存储目录项的缓冲区

  • unsigned int count: 缓冲区大小(字节)

返回值

  • 成功时返回读取的字节数

失败时返回-1,并设置errno:

  • EBADF: 文件描述符无效

  • EFAULT: 缓冲区地址无效

  • EINVAL: 参数无效

  • ENOENT: 目录不存在

相似函数

  • readdir(): 标准C库函数,更易使用

  • opendir(), closedir(): 打开和关闭目录

  • getdents64(): 64位版本,支持大文件

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <linux/types.h>

// 目录项结构体定义
struct linux_dirent {
long d_ino; // inode号
__kernel_off_t d_off; // 到下一个目录项的偏移
unsigned short d_reclen; // 当前目录项长度
char d_name&#91;]; // 文件名(以null结尾)
};

int main() {
int fd;
char buf&#91;4096];
int nread;
char *p;
struct linux_dirent *d;

printf("=== Getdents函数示例 ===\n");

// 示例1: 基本的目录读取操作
printf("\n示例1: 基本的目录读取操作\n");

// 打开当前目录
fd = open(".", O_RDONLY | O_DIRECTORY);
if (fd == -1) {
perror("打开当前目录失败");
exit(EXIT_FAILURE);
}
printf("成功打开当前目录,文件描述符: %d\n", fd);

// 使用getdents读取目录内容
printf("读取目录内容:\n");
while (1) {
nread = syscall(SYS_getdents, fd, buf, sizeof(buf));
if (nread == -1) {
perror("getdents失败");
close(fd);
exit(EXIT_FAILURE);
}

if (nread == 0) // 没有更多的目录项
break;

// 解析目录项
for (p = buf; p < buf + nread;) {
d = (struct linux_dirent *)p;

// 显示目录项信息
printf(" inode: %ld, ", d->d_ino);
printf("长度: %d, ", d->d_reclen);
printf("名称: %s\n", d->d_name);

p += d->d_reclen;
}
}

close(fd);

// 示例2: 创建测试目录和文件进行演示
printf("\n示例2: 创建测试环境进行演示\n");

// 创建测试目录
if (mkdir("test_getdents_dir", 0755) == -1 && errno != EEXIST) {
perror("创建测试目录失败");
} else {
printf("创建测试目录: test_getdents_dir\n");

// 在测试目录中创建一些文件
chdir("test_getdents_dir");

// 创建测试文件
int test_files&#91;] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
char filename&#91;20];
sprintf(filename, "test_file_%d.txt", test_files&#91;i]);
int file_fd = open(filename, O_CREAT | O_WRONLY, 0644);
if (file_fd != -1) {
const char *content = "Test file content";
write(file_fd, content, strlen(content));
close(file_fd);
printf("创建测试文件: %s\n", filename);
}
}

// 创建子目录
if (mkdir("subdir", 0755) == -1 && errno != EEXIST) {
perror("创建子目录失败");
} else {
printf("创建子目录: subdir\n");
}

// 打开测试目录
fd = open(".", O_RDONLY | O_DIRECTORY);
if (fd != -1) {
printf("\n读取测试目录内容:\n");

while (1) {
nread = syscall(SYS_getdents, fd, buf, sizeof(buf));
if (nread == -1) {
perror("getdents失败");
break;
}

if (nread == 0)
break;

// 解析并显示目录项
for (p = buf; p < buf + nread;) {
d = (struct linux_dirent *)p;

// 跳过.和..目录项
if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) {
printf(" 文件名: %-20s", d->d_name);
printf("inode: %ld, ", d->d_ino);
printf("长度: %d\n", d->d_reclen);
}

p += d->d_reclen;
}
}

close(fd);
}

// 返回上级目录并清理
chdir("..");
}

// 示例3: 与readdir的对比
printf("\n示例3: getdents与readdir对比\n");

// 使用getdents读取
fd = open(".", O_RDONLY | O_DIRECTORY);
if (fd != -1) {
printf("使用getdents读取目录:\n");
nread = syscall(SYS_getdents, fd, buf, sizeof(buf));
if (nread > 0) {
int count = 0;
for (p = buf; p < buf + nread && count < 5;) {
d = (struct linux_dirent *)p;
if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) {
printf(" %s\n", d->d_name);
count++;
}
p += d->d_reclen;
}
}
close(fd);
}

// 使用readdir读取(标准方法)
printf("使用readdir读取目录:\n");
DIR *dir = opendir(".");
if (dir != NULL) {
struct dirent *entry;
int count = 0;
while ((entry = readdir(dir)) != NULL && count < 5) {
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
printf(" %s\n", entry->d_name);
count++;
}
}
closedir(dir);
}

printf("getdents提供更多底层控制,但readdir更易使用\n");

// 示例4: 目录项详细信息解析
printf("\n示例4: 目录项详细信息解析\n");

fd = open(".", O_RDONLY | O_DIRECTORY);
if (fd != -1) {
nread = syscall(SYS_getdents, fd, buf, sizeof(buf));
if (nread > 0) {
printf("目录项详细信息:\n");
for (p = buf; p < buf + nread;) {
d = (struct linux_dirent *)p;

// 显示所有目录项(包括.和..)
printf(" 名称: %-20s", d->d_name);
printf("inode: %-10ld", d->d_ino);
printf("偏移: %-8ld", (long)d->d_off);
printf("长度: %d\n", d->d_reclen);

p += d->d_reclen;
}
}
close(fd);
}

// 示例5: 错误处理演示
printf("\n示例5: 错误处理演示\n");

// 尝试对无效文件描述符操作
nread = syscall(SYS_getdents, 999, buf, sizeof(buf));
if (nread == -1) {
printf("对无效文件描述符操作: %s\n", strerror(errno));
}

// 尝试对普通文件操作
int regular_fd = open("regular_file.txt", O_CREAT | O_WRONLY, 0644);
if (regular_fd != -1) {
write(regular_fd, "test", 4);
nread = syscall(SYS_getdents, regular_fd, buf, sizeof(buf));
if (nread == -1) {
printf("对普通文件操作: %s\n", strerror(errno));
}
close(regular_fd);
unlink("regular_file.txt");
}

// 尝试使用无效缓冲区
fd = open(".", O_RDONLY | O_DIRECTORY);
if (fd != -1) {
nread = syscall(SYS_getdents, fd, NULL, sizeof(buf));
if (nread == -1) {
printf("使用无效缓冲区: %s\n", strerror(errno));
}
close(fd);
}

// 示例6: 实际应用场景
printf("\n示例6: 实际应用场景\n");

// 场景1: 快速目录扫描(跳过隐藏文件)
printf("场景1: 快速目录扫描\n");
fd = open(".", O_RDONLY | O_DIRECTORY);
if (fd != -1) {
printf("非隐藏文件列表:\n");
int file_count = 0;
while (1) {
nread = syscall(SYS_getdents, fd, buf, sizeof(buf));
if (nread <= 0) break;

for (p = buf; p < buf + nread;) {
d = (struct linux_dirent *)p;

// 跳过.和..以及隐藏文件(以.开头)
if (strcmp(d->d_name, ".") != 0 &&
strcmp(d->d_name, "..") != 0 &&
d->d_name&#91;0] != '.') {
printf(" %s\n", d->d_name);
file_count++;
}

p += d->d_reclen;
}
}
printf("总共找到 %d 个非隐藏文件\n", file_count);
close(fd);
}

// 场景2: 目录统计信息
printf("场景2: 目录统计信息\n");
fd = open(".", O_RDONLY | O_DIRECTORY);
if (fd != -1) {
int total_files = 0;
int total_dirs = 0;
long long total_size = 0;

while (1) {
nread = syscall(SYS_getdents, fd, buf, sizeof(buf));
if (nread <= 0) break;

for (p = buf; p < buf + nread;) {
d = (struct linux_dirent *)p;

// 跳过.和..
if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) {
// 检查文件类型(需要stat)
struct stat st;
if (stat(d->d_name, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
total_dirs++;
} else {
total_files++;
total_size += st.st_size;
}
}
}

p += d->d_reclen;
}
}

printf("目录统计:\n");
printf(" 文件数: %d\n", total_files);
printf(" 目录数: %d\n", total_dirs);
printf(" 文件总大小: %lld 字节\n", total_size);

close(fd);
}

// 清理测试目录
printf("\n清理测试资源...\n");
if (access("test_getdents_dir", F_OK) == 0) {
// 删除测试目录中的文件
chdir("test_getdents_dir");
for (int i = 1; i <= 5; i++) {
char filename&#91;20];
sprintf(filename, "test_file_%d.txt", i);
unlink(filename);
}
rmdir("subdir");
chdir("..");
rmdir("test_getdents_dir");
printf("删除测试目录完成\n");
}

return 0;
}

https://www.calcguide.tech/2025/09/09/getdents-syscall-demo/

data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">

getdents64系统调用及示例

getdents系统调用及示例

data-ad-format="auto" data-full-width-responsive="true">