14

现在查看一些操作系统函数,我发现在 POSIX 系统上,您有 C 函数调用unlink(),例如删除文件、link()创建文件的硬链接、symlink()创建符号文件、移动文件,但是......文件rename()的函数在哪里copy()?

我知道通常的方法是打开源文件,读取其内容,打开目标文件并将它们转储到那里。但是为什么我找不到任何这样的效用函数呢?

4

4 回答 4

17

我尝试在 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 系统调用。希望它可以帮助你。

于 2013-07-16T02:17:04.887 回答
17

在谈论“复制文件”时,存在两种语义:

  1. Deep Copy - 创建一个新文件,其中包含与文件关联的所有数据/元数据的副本,但是文件系统对此进行了结构化
  2. 浅拷贝- 创建一个新的目录条目/文件,引用与源文件相同的数据(以及可能的部分或全部元数据)

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.dllCopyFile

在这里再次比较 Windows 和 UN*X... 并非UN*X libc 中的所有内容都是系统调用,这就是 UN*X 联机帮助页区分第 2 节(系统调用)和第 3 节(运行时库接口)的原因。Windows 上的函数也是如此kernel.dll——其中一些是“直接传递”,而另一些是更复杂的“实用函数”,不是通过单个系统调用实现的。

于 2013-07-16T09:23:40.717 回答
1

您找不到用于复制文件的实用程序功能,因为不需要以相同的方式使用它;它可以由“备件”构建。像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(),它接受格式字符串和其他参数,然后按照描述报告错误消息并退出。

于 2013-07-16T01:12:47.630 回答
0

POSIX 系统不提供像 Win32 中的 CopyFile 函数这样的系统调用,所以不要浪费时间搜索该Copy_file()函数。如果您不想重新发明轮子,我认为使用fork()创建一个新进程并调用execl(). 像这样:

execl("/bin/cp", "-p", src_file, des_file, (char *)0);
于 2013-07-16T01:34:28.720 回答