现在查看一些操作系统函数,我发现在 POSIX 系统上,您有 C 函数调用unlink()
,例如删除文件、link()
创建文件的硬链接、symlink()
创建符号文件、移动文件,但是......文件rename()
的函数在哪里copy()
?
我知道通常的方法是打开源文件,读取其内容,打开目标文件并将它们转储到那里。但是为什么我找不到任何这样的效用函数呢?
我尝试在 Linux 上的 cp 命令上运行 strace ,它实际上打开了两个文件,它从一个文件读取并以 32768 字节的块写入另一个文件:
...
stat64("log", {st_mode=S_IFREG|0644, st_size=352, ...}) = 0
stat64("copied", 0xbf99e1c0) = -1 ENOENT (No such file or directory)
open("log", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=352, ...}) = 0
open("copied", O_WRONLY|O_CREAT|O_EXCL|O_LARGEFILE, 0644) = 4
fstat64(4, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
read(3, "2012-04-27 22:26:55-0400 [-] Log"..., 32768) = 352
write(4, "2012-04-27 22:26:55-0400 [-] Log"..., 352) = 352
read(3, "", 32768) = 0
close(4) = 0
close(3) = 0
...
所以是的,那里没有 cp 系统调用。希望它可以帮助你。
在谈论“复制文件”时,存在两种语义:
Windows / DOS 文件系统传统上没有任何“浅拷贝”机制 - 但 UN*X 总是有,以硬链接的形式。
所以 POSIX/UN*X 有link(2)
系统调用 - 以新名称建立对现有“文件数据”的新引用 - 即执行浅拷贝。
“深拷贝”系统调用只有在存在“快速深拷贝”机制时才有意义——例如,在底层文件系统实现类似重复数据删除以进行文件级克隆的情况下。
否则,这样的函数将不得不“降级”(回退到)库实现。
UN*X 机制允许像这样特定于文件系统的东西ioctl()
,即“I/O 可扩展性的厨房接收器”。有关如何使用此工具(如果可用)来复制文件的示例,请参阅此 GNU coreutils 帖子,其中包含在 BTRFS 上使用文件克隆的增强请求。
鉴于 Windows'CopyFile
实际上CopyFileEx
没有回调,我强烈怀疑它真的是系统调用;这是一个实用功能。对于Wine Windows Emulator,您可以检查 kernel32.dll 源代码实现,CopyFileEx
在 Wine 源代码中查找如何dlls/kernel32/path.c
完成此操作的示例。
在微软的许可下,反汇编/反编译 Windows 的实际是不允许的,所以我不能合法地断言 Windows 本身也这样做,即是一个用户态实现,而不是一个系统调用。kernel.dll
CopyFile
在这里再次比较 Windows 和 UN*X... 并非UN*X libc 中的所有内容都是系统调用,这就是 UN*X 联机帮助页区分第 2 节(系统调用)和第 3 节(运行时库接口)的原因。Windows 上的函数也是如此kernel.dll
——其中一些是“直接传递”,而另一些是更复杂的“实用函数”,不是通过单个系统调用实现的。
您找不到用于复制文件的实用程序功能,因为不需要以相同的方式使用它;它可以由“备件”构建。像unlink()
和symlink()
不能根据其他函数构建的函数,而诸如假设函数之类的函数copy_file()
可以(所以你必须)。
给定两个打开的文件流,f1
用于读取和f2
写入,那么您可以使用:
void fcopy(FILE *f1, FILE *f2)
{
char buffer[BUFSIZ];
size_t n;
while ((n = fread(buffer, sizeof(char), sizeof(buffer), f1)) > 0)
{
if (fwrite(buffer, sizeof(char), n, f2) != n)
err_syserr("write failed\n");
}
}
该err_syserr()
函数用于错误报告,包括作为参数传递的字符串和errno
*隐含的错误消息;它不返回。 BUFSIZ
在中定义,<stdio.h>
但您可以选择使用更大的值。您可能不希望这样的错误报告,而是让函数在成功时返回 0,在任何失败时返回 -1。
int fcopy(FILE *f1, FILE *f2)
{
char buffer[BUFSIZ];
size_t n;
while ((n = fread(buffer, sizeof(char), sizeof(buffer), f1)) > 0)
{
if (fwrite(buffer, sizeof(char), n, f2) != n)
return -1;
}
return 0;
}
请注意,由于该函数不会打开文件,因此也不会关闭它们。例如,这意味着您可以使用它将多个输入文件连接到单个输出文件。您可以使用包装函数打开一个文件进行读取和另一个写入。
* 实际上,err_syserr()
是一个类似的函数printf()
,它接受格式字符串和其他参数,然后按照描述报告错误消息并退出。
POSIX 系统不提供像 Win32 中的 CopyFile 函数这样的系统调用,所以不要浪费时间搜索该Copy_file()
函数。如果您不想重新发明轮子,我认为使用fork()
创建一个新进程并调用execl()
. 像这样:
execl("/bin/cp", "-p", src_file, des_file, (char *)0);