使用copy_file_range复制加速

时间:2019-01-22 06:14:51

标签: c linux file-descriptor

我正在学习Linux中两个文件描述符之间的内核内数据传输,遇到了一些我不了解的东西。这是copy_file_range联机帮助页中的引用

  

copy_file_range()使文件系统有机会实施“复制   加速”技术,例如使用reflink(即两个或两个   共享指向同一写时复制磁盘的指针的更多i节点   块)或服务器端复制

我曾经认为索引节点是stat / statx系统调用返回的内容。 st_ino类型被typedef编辑为here

typedef unsigned long   __kernel_ulong_t;

那么“两个或多个共享指向相同的写时复制磁盘块的指针的i节点”到底是什么意思?

1 个答案:

答案 0 :(得分:2)

据我了解,copy_file_range不需要通过用户模式传递数据这一事实意味着内核根本不需要从磁盘加载数据(它仍然可以,但是(不必),这可以通过将操作向下推到文件系统堆栈中来进行进一步优化。这涵盖了通过NFS进行服务器端复制的情况。

有关其他优化的实际答案首先是文件存储方式的介绍,如果您已经知道,可以跳过它。

在典型的Linux FS中,文件的存储方式分为3层:

  1. 某个目录中的文件条目(本身就是包含此类条目列表的文件)。这样的条目实际上将文件名映射到某个索引节点。通过将inode编号又称为st_ino来存储它,它实际上是指向某些表中inode的指针。

  2. inode,其中包含一些共享的(请参阅其他内容)元数据(如stat返回的元数据)和一些指向存储实际文件的数据块的指针内容。

  3. 实际数据块

因此,例如,硬链接是某个目录中的一条记录,该记录指向与“原始”文件相同的inode(并在inode中增加“ link counter”)。这意味着只有文件名(可能还有目录)是不同的,其余所有数据和元数据在硬链接之间共享。请注意,创建硬链接是复制文件的非常快速的方法。唯一的缺点是,两个文件现在都必须永远共享其内容,因此这不是真正的副本。但是,如果我们使用某种copy-on-write方法来修复“写”部分,它将非常有用。这就是某些FS(例如Btrfs)通过reflink支持的功能。

这种写时复制技巧的想法是,您可以使用新的适当的元数据创建一个新的inode,但仍共享相同的数据块。您还可以在inode元数据的“不可见”部分中的两个inode之间添加交叉引用,以便它们知道它们共享数据块。显然,与实际复制相比,此操作非常快。再一次,只要只读取文件,一切都将完美运行。但是与硬链接不同,我们可以处理将它们视为独立的写入。执行某些写操作后,FS会检查文件(或更确切地说是inode)是否确实是数据块的唯一所有者,并在写入之前复制数据。根据FS的实现,它可以在第一次写入时复制整个文件,或者可以存储一些更详细的元数据,并且仅复制必须修改的块,并且仍然在文件之间共享其余的块。在后一种情况下,如果写入大小大于一个块,则可能根本不需要复制块。

因此,copy_file_range()最简单的技巧就是检查整个文件是否确实被复制,如果是,则执行上述的reflink技巧(显然,如果FS支持)。

如果FS支持数据块上更详细的元数据,则还可以进行一些更高级的优化。假设您从文件开头将前N个字节复制到新文件中。然后,FS可以仅共享起始块,并且可能只需要复制未完全复制的最后一个块。