常见的零拷贝优化技术

Posted 2021-09-09 14:40 +0800 by ZhangJie ‐ 1 min read

分享:  

在关注IO性能时我们经常听到零拷贝,那么零拷贝到底是什么呢?为什么要做零拷贝?又有哪些方案?本文就一起来看下。

零拷贝技术一般可以分为两类

  • devices(disk、nic)和kernel buffer之间的数据拷贝,一般可以通过DMA(直接存储器访问)来优化掉,既能够避免中断CPU减轻CPU负载,也能够直接读写内存减少数据从nic到cpu再到kernel buffer的拷贝动作;
  • kernel buffer和application buffer之间的数据拷贝;

说优化拷贝一般是去优化cpu拷贝,DMA拷贝是无法避免的。零拷贝则强调的是kernel buffer和application buffer之间的拷贝,零拷贝并不是说整个过程中完全有没有数据拷贝,在kernel space还是发生拷贝,当然下面提到的sendfile+DMA硬件支持分散读/聚集写情况下能优化掉kernel buffer之间的拷贝。

然后明确下优化拷贝的原因

  • 使用系统调用的次数影响到上下文切换次数,上下文切换会带来一定的开销,如read、write组合起来完成磁盘数据读取、网络发送,就要切换4次,而且read、write要重复很多次,上下文切换开销就不能完全忽视;
  • 数据拷贝,主要是说利用cpu来拷贝,cpu势必要中断原来的任务去做拷贝的事情,move来move去,干了些杂活,理想情况下是希望尽可能做更多的事,当然不一定能完全避免cpu拷贝,但是能让拷贝的数据量减少点还是值得的;

零拷贝优化一方面是要优化掉kernel buffer和application buffer的数据拷贝问题,一方面也要考虑下如何尽可能减少cpu拷贝对程序停顿的影响。

常见的拷贝优化方案

这里解决kernel buffer和application buffer之间数据拷贝的常用办法有以下几种,以读取磁盘数据发送到socket为例说明:

memory mapping

mmap系统调用,read的时候,dma从磁盘发送数据到kernel buffer,mmap根据fd映射对应的kernel buffer和application buffer,省掉一次考拷贝。write的时候,数据从kernel buffer直接拷贝到socket buffer再到nic buffer;

shared buffers in kernel memory space

这里希望能再次优化掉mmap方案中write时从kernel buffer到nic buffer的拷贝,在kernel space中建立一个共享内存区域buf,dma传送数据到这个buf的b_data指针指向的位置,write的时候使用dma从这个buf的m_data位置开始写,其实b_data、m_data共享了底层内存区域。相当于一个写指针、一个读指针。通过这种方式优化掉了kernel space到nic space的拷贝;

shared buffers between user and kernel space

linux sk_buffers结构,其中有个指针记录着要发送的数据(application buffer中)的地址,避免了从application buffer到kernel buffer的拷贝;

different system calls, sendfile, splice, etc

先说sendfile,sendfile允许在fd之间直接传送数据,进出sendfile上下文切换只需两次,数据直接从源fd对应的kernel buffer(dma从设备上拷贝过去)到目的socketfd的socket buffer拷贝,完全绕过了应用程序buffer及其拷贝;

sendfile with DMA Scatter/Gather copy

上面sendfile存在一个从kernel buffer到socket buffer的cpu拷贝,如何优化掉?在硬件支持下,kernel buffer可以只把这个buffer的fd信息发送给socket,这里就避免了数据拷贝,这里的kernel buffer可以是多个,那就发送多个buffer的fd信息给socket,然后DMA借助分散读聚集写直接从上述kernel buffers拷贝到nic buffer。这样完全消除了cpu拷贝动作,避免了对cpu的中断;

splice

从一个fd到另一个fd拷贝数据,先要在两个fd之间通过pipe系统调用构建一个管道,管道在内核中就是一个buffer只不过返回读端、写端在userspace中供读写。splice就是从源fd的kernel buffer写到pipe buffer的写端,然后再从这个pipe buffer的读端拷贝数据到目的fd的kernel buffer。和前面最牛对技术方案相比,这种方案和前面这种sendfile+DMA分散读聚集写相比,kernel space中多了两次cpu copy,好处是可以不需要硬件的支持;

hardware support

前面已经提到过了DMA相关的加成,可能还有其他方案;

总结

本文总结了零拷贝是什么、目的是什么以及有哪些常见的优化手段,除了软件层面的方案,在硬件加成下还可以做更好的方案。其实不同零拷贝技术对安全加密、过滤也有不同的影响,这部分内容如果有机会再总结分享。

参考内容

  • https://www.uidaho.edu/-/media/UIdaho-Responsive/Files/engr/research/csds/publications/2012/Performance-Review-of-Zero-Copy-Techniques-2012.pdf
  • mmap:https://man7.org/linux/man-pages/man2/mmap.2.html
  • splice/tee:https://www.kernelhcy.info/?p=202
  • splice: https://man7.org/linux/man-pages/man2/splice.2.html