Linux内核splice()是否零拷贝?

我知道splice()被devise为零拷贝,并使用Linux内核pipe道缓冲区来实现。 例如,如果我想将数据从一个文件描述符(fp1)复制到另一个文件描述符(fp2),则不需要从“内核空间 – >用户空间 – >内核空间”复制数据。 相反,它只是在内核空间复制数据,stream程将如“fp1 – > pipe_read – > pipe_write – > fp2”。 我的问题是剂量内核需要在“fp1 – > pipe_read”和“pipe_write – > fp2”之间复制数据?

维基百科说:

Ideally, splice and vmsplice work by remapping pages and do not actually copy any data, which may improve I/O performance. As linear addresses do not necessarily correspond to contiguous physical addresses, this may not be possible in all cases and on all hardware combinations. 

我已经为我的问题追踪了内核源码(3.12) ,并且发现了“fp1-> write_pipe”之间的stream程,最后它将在fs / splice.c中调用kernel_readv(),然后调用“do_readv_writev()”和最后称为“aio_write()”

 558 static ssize_t kernel_readv(struct file *file, const struct iovec *vec, 559 unsigned long vlen, loff_t offset) //*vec would point to struct page which belong to pipe 

最后的“read_pipe – > fp2”之间的stream程将调用“__kernel_write()”,然后调用“fp2-> f_op-> write()”

 430 ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos) //*buf is the pipe buffer 

我认为“aio_write()”和“file-> f_op_write()”将执行真正的数据复制,splice()是否真的执行零拷贝?

据我了解拼接(),它会读取fd1的页面,MMU将映射这些页面。 映射创建的引用将被放入管道并交给fd2。 只要每个参与者都有可用的DMA,就不应该复制实际的数据。 如果没有DMA可用,则需要复制数据。

splice最有可能的工作是零拷贝(对此没有硬性保证,但对于任何合理的近期硬件,它几乎肯定会这样工作)。 严格遵循这个文档,你需要用SPLICE_F_MOVE来调用它,所以没有实际的拷贝,但是只要有DMA支持(这是一个相当公平的假设),我不知道怎么做才能做到这一点。

vmsplice 也不一定是这样,因为如果提供了SPLICE_F_GIFT标志(或者一个连续的splice ),它只能工作于零拷贝(在这种情况下,我可以看到它是不会起作用的,因为“源描述符”是主要的内存),但是这个标志在其他Linux版本中被破坏了一些,并且在其他Linux版本中不被支持,并且在顶部被记录得很差。
例如,不清楚随后如何处理内存。 这个文档曾经说过,你不允许触及天赋的记忆,这个记忆最近被稍微改了一下,但是并不是那么含糊不清。 目前还不清楚内存区域将变成什么样子。 在文档之后,你将不得不泄漏内存。 似乎没有通知机制告诉你何时可以安全地释放内存或重用它。

aio_write是使用线程和write系统调用的异步I / O的用户域(Glibc)实现。 这通常执行至少一个从用户空间到内核空间的副本。