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[]; // 文件名(以null结尾) };
int main() { int fd; char buf[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[] = {1, 2, 3, 4, 5}; for (int i = 0; i < 5; i++) { char filename[20]; sprintf(filename, "test_file_%d.txt", test_files[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[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[20]; sprintf(filename, "test_file_%d.txt", i); unlink(filename); } rmdir("subdir"); chdir(".."); rmdir("test_getdents_dir"); printf("删除测试目录完成\n"); } return 0; }
|