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

VTx*_*yer 6 c linux linux-kernel

我知道 splice() 是为零复制而设计的,并使用 Linux 内核管道缓冲区来实现这一点。例如,如果我想将数据从一个文件描述符(fp1)复制到另一个文件描述符(fp2),则不需要从“内核空间->用户空间->内核空间”复制数据。相反,它只是在内核空间中复制数据,流程将类似于“fp1 -> pipeline_read -> pipeline_write -> fp2”。我的问题是剂量内核需要在“fp1 -> pipeline_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.
Run Code Online (Sandbox Code Playgroud)

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

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.
Run Code Online (Sandbox Code Playgroud)

“read_pipe -> fp2”之间的流程最终会调用“__kernel_write()”,然后调用“fp2->f_op->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
Run Code Online (Sandbox Code Playgroud)

我认为“aio_write()”和“file->f_op_write()”都会执行真正的数据复制,那么 splice() 真的执行零复制吗?

iks*_*eam 3

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