当前位置: 首页 > java >正文

fchdir系统调用及示例

20. fchdir - 通过文件描述符改变当前工作目录

函数介绍

fchdir系统调用用于通过已打开的目录文件描述符来改变进程的当前工作目录。相比chdir,它避免了路径名解析,更加高效和安全。

函数原型

#include <unistd.h>int fchdir(int fd);

功能

通过目录文件描述符改变进程当前工作目录。

参数

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

返回值

  • 成功时返回0
  • 失败时返回-1,并设置errno:
    • EBADF: 文件描述符无效或不是目录
    • EACCES: 权限不足
    • EIO: I/O错误
    • ENOMEM: 内存不足

相似函数

  • chdir(): 通过路径名改变当前工作目录
  • getcwd(): 获取当前工作目录

示例代码

#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 <limits.h>
#include <dirent.h>int main() {char buffer[PATH_MAX];char original_dir[PATH_MAX];int dir_fd;printf("=== Fchdir函数示例 ===\n");// 保存原始目录if (getcwd(original_dir, sizeof(original_dir)) == NULL) {perror("获取原始目录失败");exit(EXIT_FAILURE);}printf("原始工作目录: %s\n", original_dir);// 示例1: 基本的fchdir操作printf("\n示例1: 基本的fchdir操作\n");// 打开根目录dir_fd = open("/", O_RDONLY);if (dir_fd == -1) {perror("打开根目录失败");} else {printf("成功打开根目录,文件描述符: %d\n", dir_fd);// 使用fchdir切换到根目录if (fchdir(dir_fd) == -1) {perror("fchdir到根目录失败");} else {printf("成功使用fchdir切换到根目录\n");if (getcwd(buffer, sizeof(buffer)) != NULL) {printf("当前目录: %s\n", buffer);}}close(dir_fd);}// 返回原始目录if (chdir(original_dir) == -1) {perror("返回原始目录失败");} else {printf("成功返回原始目录\n");}// 示例2: 创建测试环境printf("\n示例2: 创建测试环境\n");// 创建测试目录结构const char *test_base = "test_fchdir_base";const char *subdir1 = "test_fchdir_base/subdir1";const char *subdir2 = "test_fchdir_base/subdir2";// 创建目录if (mkdir(test_base, 0755) == -1 && errno != EEXIST) {perror("创建基础目录失败");} else {printf("创建基础目录: %s\n", test_base);}if (mkdir(subdir1, 0755) == -1 && errno != EEXIST) {perror("创建子目录1失败");} else {printf("创建子目录1: %s\n", subdir1);}if (mkdir(subdir2, 0755) == -1 && errno != EEXIST) {perror("创建子目录2失败");} else {printf("创建子目录2: %s\n", subdir2);}// 在目录中创建测试文件char test_file_path[PATH_MAX * 2];snprintf(test_file_path, sizeof(test_file_path), "%s/test_file.txt", test_base);int fd = open(test_file_path, O_CREAT | O_WRONLY, 0644);if (fd != -1) {const char *content = "Test file for fchdir demonstration";write(fd, content, strlen(content));close(fd);printf("创建测试文件: %s\n", test_file_path);}// 示例3: fchdir与chdir对比printf("\n示例3: fchdir与chdir对比\n");// 使用chdir切换clock_t chdir_start = clock();for (int i = 0; i < 1000; i++) {chdir(test_base);chdir(original_dir);}clock_t chdir_time = clock() - chdir_start;// 使用fchdir切换(需要先打开目录)int test_dir_fd = open(test_base, O_RDONLY);if (test_dir_fd != -1) {clock_t fchdir_start = clock();for (int i = 0; i < 1000; i++) {fchdir(test_dir_fd);fchdir(dir_fd);  // dir_fd是之前打开的根目录}clock_t fchdir_time = clock() - fchdir_start;printf("1000次chdir操作耗时: %f 秒\n", ((double)chdir_time) / CLOCKS_PER_SEC);printf("1000次fchdir操作耗时: %f 秒\n", ((double)fchdir_time) / CLOCKS_PER_SEC);printf("fchdir通常比chdir更快(避免路径解析)\n");close(test_dir_fd);}// 示例4: 使用目录文件描述符的优势printf("\n示例4: 使用目录文件描述符的优势\n");// 打开测试目录dir_fd = open(test_base, O_RDONLY);if (dir_fd == -1) {perror("打开测试目录失败");} else {printf("打开测试目录,文件描述符: %d\n", dir_fd);// 使用fchdir切换if (fchdir(dir_fd) == -1) {perror("fchdir失败");} else {printf("使用fchdir切换成功\n");if (getcwd(buffer, sizeof(buffer)) != NULL) {printf("当前目录: %s\n", buffer);}// 在当前目录创建文件fd = open("file_via_fchdir.txt", O_CREAT | O_WRONLY, 0644);if (fd != -1) {const char *file_content = "File created after fchdir";write(fd, file_content, strlen(file_content));close(fd);printf("在切换后的目录创建文件\n");}}// 即使目录被重命名或删除,文件描述符仍然有效printf("演示文件描述符的持久性:\n");printf("  即使目录被重命名,fchdir仍然可以工作\n");close(dir_fd);}// 返回原始目录if (chdir(original_dir) == -1) {perror("返回原始目录失败");}// 示例5: 使用opendir和dirfdprintf("\n示例5: 使用opendir和dirfd\n");// 使用opendir打开目录DIR *dir = opendir(test_base);if (dir == NULL) {perror("opendir失败");} else {printf("使用opendir打开目录成功\n");// 获取目录文件描述符int dirfd_from_opendir = dirfd(dir);printf("目录文件描述符: %d\n", dirfd_from_opendir);// 使用fchdirif (fchdir(dirfd_from_opendir) == -1) {perror("使用dirfd的fchdir失败");} else {printf("使用dirfd的fchdir成功\n");if (getcwd(buffer, sizeof(buffer)) != NULL) {printf("当前目录: %s\n", buffer);}}closedir(dir);}// 返回原始目录if (chdir(original_dir) == -1) {perror("返回原始目录失败");}// 示例6: 安全的目录切换模式printf("\n示例6: 安全的目录切换模式\n");// 保存当前目录的文件描述符int current_dir_fd = open(".", O_RDONLY);if (current_dir_fd == -1) {perror("保存当前目录失败");} else {printf("保存当前目录文件描述符: %d\n", current_dir_fd);// 切换到测试目录dir_fd = open(test_base, O_RDONLY);if (dir_fd != -1) {if (fchdir(dir_fd) == -1) {perror("切换到测试目录失败");} else {printf("切换到测试目录\n");if (getcwd(buffer, sizeof(buffer)) != NULL) {printf("当前目录: %s\n", buffer);}// 执行一些操作system("ls -la");// 安全返回(使用保存的文件描述符)if (fchdir(current_dir_fd) == -1) {perror("安全返回失败");} else {printf("安全返回原始目录\n");if (getcwd(buffer, sizeof(buffer)) != NULL) {printf("当前目录: %s\n", buffer);}}}close(dir_fd);}close(current_dir_fd);}// 示例7: 错误处理演示printf("\n示例7: 错误处理演示\n");// 尝试对无效文件描述符使用fchdirif (fchdir(999) == -1) {printf("对无效文件描述符使用fchdir: %s\n", strerror(errno));}// 尝试对普通文件使用fchdirint regular_fd = open("regular_file.txt", O_CREAT | O_WRONLY, 0644);if (regular_fd != -1) {write(regular_fd, "test", 4);if (fchdir(regular_fd) == -1) {printf("对普通文件使用fchdir: %s\n", strerror(errno));}close(regular_fd);unlink("regular_file.txt");}// 尝试对已关闭的文件描述符使用fchdirdir_fd = open(test_base, O_RDONLY);if (dir_fd != -1) {close(dir_fd);if (fchdir(dir_fd) == -1) {printf("对已关闭的文件描述符使用fchdir: %s\n", strerror(errno));}}// 示例8: 实际应用场景printf("\n示例8: 实际应用场景\n");// 场景1: 多线程环境中的目录操作printf("场景1: 多线程安全的目录操作\n");printf("说明: 在多线程环境中,使用文件描述符比路径名更安全\n");printf("因为文件描述符不受其他线程改变当前目录的影响\n");// 场景2: 高频目录切换优化printf("场景2: 高频目录切换优化\n");// 预打开常用目录struct {const char *name;int fd;} common_dirs[] = {{"根目录", -1},{"测试目录", -1},{"用户主目录", -1}};// 打开常用目录common_dirs[0].fd = open("/", O_RDONLY);common_dirs[1].fd = open(test_base, O_RDONLY);common_dirs[2].fd = open(getenv("HOME") ? getenv("HOME") : "/", O_RDONLY);printf("预打开常用目录:\n");for (int i = 0; i < 3; i++) {if (common_dirs[i].fd != -1) {printf("  %s: fd=%d\n", common_dirs[i].name, common_dirs[i].fd);}}// 快速切换演示printf("快速目录切换演示:\n");for (int i = 0; i < 3; i++) {if (common_dirs[i].fd != -1) {if (fchdir(common_dirs[i].fd) == 0) {if (getcwd(buffer, sizeof(buffer)) != NULL) {printf("  切换到%s: %s\n", common_dirs[i].name, buffer);}}}}// 清理预打开的目录for (int i = 0; i < 3; i++) {if (common_dirs[i].fd != -1) {close(common_dirs[i].fd);}}// 场景3: 临时目录操作printf("场景3: 临时目录操作\n");char temp_dir[] = "/tmp/fchdir_test_XXXXXX";if (mkdtemp(temp_dir) != NULL) {printf("创建临时目录: %s\n", temp_dir);// 打开临时目录int temp_fd = open(temp_dir, O_RDONLY);if (temp_fd != -1) {printf("打开临时目录,fd=%d\n", temp_fd);// 切换到临时目录if (fchdir(temp_fd) == 0) {printf("切换到临时目录\n");// 在临时目录中创建工作system("echo 'Work in temporary directory' > work.txt");system("ls -la");// 完成后返回if (chdir(original_dir) == 0) {printf("返回原始目录\n");}}close(temp_fd);}// 清理临时目录char cleanup_cmd[PATH_MAX + 50];snprintf(cleanup_cmd, sizeof(cleanup_cmd), "rm -rf %s", temp_dir);system(cleanup_cmd);}// 场景4: 权限受限环境printf("场景4: 权限受限环境\n");printf("说明: fchdir可以在某些权限受限的情况下工作\n");printf("当进程有目录的文件描述符但没有路径遍历权限时\n");// 示例9: 性能测试printf("\n示例9: 性能测试\n");// 测试fchdir性能dir_fd = open(test_base, O_RDONLY);if (dir_fd != -1) {current_dir_fd = open(".", O_RDONLY);if (current_dir_fd != -1) {const int iterations = 10000;clock_t start = clock();for (int i = 0; i < iterations; i++) {fchdir(dir_fd);fchdir(current_dir_fd);}clock_t fchdir_total = clock() - start;printf("%d次fchdir操作耗时: %f 秒\n", iterations * 2, ((double)fchdir_total) / CLOCKS_PER_SEC);printf("平均每次fchdir耗时: %f 微秒\n", (((double)fchdir_total) / CLOCKS_PER_SEC * 1000000) / (iterations * 2));close(current_dir_fd);}close(dir_fd);}// 示例10: 与符号链接的交互printf("\n示例10: 与符号链接的交互\n");// 创建符号链接目录const char *symlink_name = "test_fchdir_base/symlink_to_subdir1";if (symlink("subdir1", symlink_name) == -1 && errno != EEXIST) {perror("创建符号链接失败");} else {printf("创建符号链接: %s -> subdir1\n", symlink_name);// 通过符号链接打开目录int symlink_fd = open(symlink_name, O_RDONLY);if (symlink_fd != -1) {printf("通过符号链接打开目录,fd=%d\n", symlink_fd);// 使用fchdir(跟随符号链接)if (fchdir(symlink_fd) == 0) {if (getcwd(buffer, sizeof(buffer)) != NULL) {printf("fchdir后的目录: %s\n", buffer);}}close(symlink_fd);}// 返回并清理if (chdir(original_dir) == 0) {if (chdir(test_base) == 0) {unlink(symlink_name);chdir(original_dir);}}}// 清理测试资源printf("\n清理测试资源...\n");// 删除测试目录中的文件char file_to_delete[PATH_MAX * 2];snprintf(file_to_delete, sizeof(file_to_delete), "%s/%s/file_via_fchdir.txt", original_dir, test_base);if (access(file_to_delete, F_OK) == 0) {unlink(file_to_delete);}snprintf(file_to_delete, sizeof(file_to_delete), "%s/%s/test_file.txt", original_dir, test_base);if (access(file_to_delete, F_OK) == 0) {unlink(file_to_delete);}// 删除测试目录结构char subdir1_path[PATH_MAX * 2];snprintf(subdir1_path, sizeof(subdir1_path), "%s/%s", original_dir, subdir1);if (access(subdir1_path, F_OK) == 0) {rmdir(subdir1_path);}char subdir2_path[PATH_MAX * 2];snprintf(subdir2_path, sizeof(subdir2_path), "%s/%s", original_dir, subdir2);if (access(subdir2_path, F_OK) == 0) {rmdir(subdir2_path);}char base_dir_path[PATH_MAX * 2];snprintf(base_dir_path, sizeof(base_dir_path), "%s/%s", original_dir, test_base);if (access(base_dir_path, F_OK) == 0) {rmdir(base_dir_path);printf("删除测试目录结构完成\n");}return 0;
}
http://www.xdnf.cn/news/16465.html

相关文章:

  • Git+宝塔面板部署Hugo博客
  • CodeBLEU:面向代码合成的多维度自动评估指标——原理、演进与开源实践
  • 三色标记法
  • Spring经典“送命题”:BeanFactory vs FactoryBean
  • GPT 生成一个打字练习页面
  • 基于LNMP架构的分布式个人博客搭建
  • Mysql中的索引详解
  • 浅谈智能体经济(上篇)——人机共生、生态自治的未来经济形态
  • JavaScript单线程实现异步
  • 「iOS」————MRC
  • Python爬虫实战:研究netaddr库相关技术构建IP地址信息采集分析系统
  • 【NLP实践】三、LLM搭建中文知识库:提供RestfulAPI服务
  • 解决GoLand运行go程序报错:Error: Cannot find package xxx 问题
  • 3.JDK+JRE组件构成与协作
  • Qt 窗口 工具栏QToolBar、状态栏StatusBar
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现标签条码一维码的检测(C#代码,UI界面版)
  • 基于Django的天气数据可视化分析预测系统
  • 背包DP之多重背包
  • python 阿里云 安装 dashscope的简介、安装
  • 【数据结构与算法】数据结构初阶:详解排序(二)——交换排序中的快速排序
  • 算法竞赛阶段二-数据结构(36)数据结构双向链表模拟实现
  • bash的特性-bash中的引号
  • 力扣131:分割回文串
  • 智能化设备健康管理:中讯烛龙预测性维护系统引领行业变革
  • 零基础,如何入手学习SAP?
  • ASP.NET Core 高并发万字攻防战:架构设计、性能优化与生产实践
  • OpenLayers 综合案例-地图绘制
  • 使用低级上位画图法理解在对磁盘空间进行容量分配时【低级单位上位至高级单位的换算】
  • 【论文阅读】ON THE ROLE OF ATTENTION HEADS IN LARGE LANGUAGE MODEL SAFETY
  • Flutter开发实战之CI/CD与发布流程