共计 2282 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点
文件复制是系统编程中最基础却容易出问题的操作之一。常见挑战包括:

- 大文件处理:一次性读取整个文件可能导致内存耗尽
- 权限控制:目标文件的默认权限可能不符合预期(如 777 权限的安全风险)
- 错误恢复:复制中途失败时缺乏恢复机制
- 性能问题:小缓冲区导致频繁系统调用(如 1 字节缓冲区复制 1GB 文件需 10 亿次调用)
技术方案对比
| 实现方式 | 优点 | 缺点 |
|---|---|---|
| 流式读写 | 内存友好,适合大文件 | 需手动处理缓冲区 |
| mmap 内存映射 | 零拷贝,性能最佳 | 文件大小受限于虚拟内存 |
| sendfile | 内核层零拷贝 | 仅适用于本地文件系统 |
| 库函数(fcopy) | 简单易用 | 缺乏细粒度控制 |
核心实现
命令行参数解析
使用 getopt 规范处理参数,典型错误处理流程:
if (argc < 3) {fprintf(stderr, "Usage: %s <src> <dst>\n", argv[0]);
exit(EXIT_FAILURE);
}
文件打开模式
关键标志位组合:
O_WRONLY:只写模式O_CREAT:不存在时创建O_TRUNC:存在时清空
int dst_fd = open(dst_path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (dst_fd == -1) {perror("open dst");
exit(EXIT_FAILURE);
}
权限控制(0644 详解)
权限位分解:
6 4 4 (八进制)
110 100 100 (二进制)
rw- r-- r-- (文字表示)
- 所有者:读写
- 组用户:只读
- 其他用户:只读
缓冲区读写实现
推荐 8KB 缓冲区(大多数文件系统块大小的整数倍):
#define BUF_SIZE 8192
char buf[BUF_SIZE];
ssize_t bytes_read;
while ((bytes_read = read(src_fd, buf, BUF_SIZE)) > 0) {if (write(dst_fd, buf, bytes_read) != bytes_read) {perror("write");
exit(EXIT_FAILURE);
}
}
完整代码示例
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#define BUF_SIZE 8192
int main(int argc, char *argv[]) {if (argc < 3) {fprintf(stderr, "Usage: %s <src> <dst>\n", argv[0]);
return EXIT_FAILURE;
}
const char *src_path = argv[1];
const char *dst_path = argv[2];
int src_fd, dst_fd;
ssize_t bytes_read;
char buf[BUF_SIZE];
// 打开源文件
if ((src_fd = open(src_path, O_RDONLY)) == -1) {perror("open src");
return EXIT_FAILURE;
}
// 创建 / 截断目标文件
if ((dst_fd = open(dst_path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {perror("open dst");
close(src_fd);
return EXIT_FAILURE;
}
// 复制循环
while ((bytes_read = read(src_fd, buf, BUF_SIZE)) > 0) {ssize_t bytes_written = write(dst_fd, buf, bytes_read);
if (bytes_written != bytes_read) {perror("write");
close(src_fd);
close(dst_fd);
return EXIT_FAILURE;
}
}
// 检查读取错误
if (bytes_read == -1) {perror("read");
close(src_fd);
close(dst_fd);
return EXIT_FAILURE;
}
// 关闭文件描述符
if (close(src_fd) == -1 || close(dst_fd) == -1) {perror("close");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
性能优化
缓冲区大小对性能的影响测试(复制 1GB 文件):
| 缓冲区大小 | 系统调用次数 | 耗时(秒) |
|---|---|---|
| 512B | 2,097,152 | 12.7 |
| 4KB | 262,144 | 3.2 |
| 8KB | 131,072 | 1.8 |
| 1MB | 1,024 | 1.5 |
建议选择 4KB-64KB 之间的值(平衡内存使用和性能)
避坑指南
- 符号链接风险
- 使用
O_NOFOLLOW标志避免跟随符号链接 -
或先
lstat()检查文件类型 -
跨文件系统问题
- 大文件可能超过目标文件系统的剩余空间
-
某些特殊属性(如扩展属性)可能无法保留
-
资源泄露预防
- 每个
open()必须对应close() - 错误路径中确保释放所有资源
- 考虑使用
scoped_fd等 RAII 包装器
延伸思考
目录复制实现思路
- 使用
opendir()/readdir()遍历目录 - 对每个文件项递归调用复制函数
- 使用
mkdir()创建目标子目录
断点续传增强方案
- 记录已复制的字节偏移量
- 目标文件以
O_APPEND模式打开 - 源文件使用
lseek()定位到中断位置
[src] --read--> [buffer] --write--> [dst]
↑ ↓
└──error handling───┘
通过正确处理错误条件和优化 IO 路径,这个基础文件复制工具可以达到生产环境要求的可靠性和性能。后续可扩展为支持通配符匹配、进度显示等实用功能。
正文完
发表至: 未分类
近三天内
