首页 > > > Linux 中的零拷贝技术
要知道零拷贝,需要先知道什么是拷贝。其实这个”拷贝“来自于 Linux 中的标准 I/O 接口的基本数据拷贝的操作,而这一操作会导致数据在操作系统内核地址空间的缓冲区和应用程序地址空间定义的缓冲区之间进行传输。

从上图中可以看出,共产生了四次数据拷贝,即使使用了DMA来处理了与硬件的通讯,CPU仍然需要处理两次数据拷贝,与此同时,在用户态与内核态也发生了多次上下文切换,无疑也加重了CPU负担。
在此过程中,我们没有对文件内容做任何修改,那么在内核空间和用户空间来回拷贝数据无疑就是一种浪费,而零拷贝主要就是为了解决这种低效性。
概括来说,原因为以下两点:
既然传统的 I/O 操作有性能问题,那么如何改进呢?我们将其定义为“零拷贝技术”。
事实上零拷贝主要指的是避免数据拷贝,而非没有拷贝。大致概括如下:
应用程序直接访问硬件存储,操作系统内核只是辅助数据传输。

应用程序调用了 mmap() 之后,数据会先通过 DMA 拷贝到操作系统内核的缓冲区中去。接着,应用程序跟操作系统共享这个缓冲区,这样,操作系统内核和应用程序存储空间就不需要再进行任何的数据拷贝操作。

利用 DMA 引擎将文件中的数据拷贝到操作系统内核缓冲区中,然后数据被拷贝到与 socket 相关的内核缓冲区中去。接下来,DMA 引擎将数据从内核 socket 缓冲区中拷贝到协议引擎中去。

这里还是有一次 CPU 拷贝,怎么把这个也省掉呢?继续看——
之前我们是把页缓存的数据拷贝到socket缓存中,实际上,我们仅仅需要把缓冲区描述符传到 socket 缓冲区,再把数据长度传过去,这样 DMA 控制器直接将页缓存中的数据打包发送到网络中就可以了。

上图总结为以下 3 步:
sendfile 利用了Linux提出的管道缓冲区机制,只适用于将数据从文件拷贝到套接字上,有一方必须是管道设备,因而限定了它的使用范围。
以上几种零拷贝技术都是减少数据在用户空间和内核空间拷贝技术实现的,但是有些时候,数据必须在用户空间和内核空间之间拷贝。这时候,我们只能针对数据在用户空间和内核空间拷贝的时机上下功夫了。
一般方法有:
这里不再细说,感兴趣的可以阅读后面的参考文章深入了解。
在实际应用中,尽管 sendfile 有一些缺点(比如缺少标准实现等),但依然是一个非常有用的功能。Kafka 的消息消费机制就是使用 sendfile —— 严格来说是通过 Java 的 FileChannel.transferTo 方法来实现的。
本文首发于公众号“数据Man”,欢迎关注!

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69918097/viewspace-3122752/ ,如需转载,请注明出处,否则将追究法律责任。