如何深入理解零拷贝技术

零拷贝技术是一种思想 , 指的是计算机操作时 , CPU不需要先将数据从某处内存复制从某处内存复制到另一个特定区域 。可见 , 零拷贝的特点是 CPU 不全程负责内存中的数据写入其他组件 , CPU 仅仅起到管理的作用 。但注意 , 零拷贝不是不进行拷贝 , 而是 CPU 不再全程负责数据拷贝时的搬运工作 。如果数据本身不在内存中 , 那么必须先通过某种方式拷贝到内存中(这个过程 CPU 可以不参与) , 因为数据只有在内存中 , 才能被转移 , 才能被 CPU 直接读取计算 。
零拷贝技术的具体实现方式有很多 , 例如:
 

  • mmap(内存映射) + write
  • sendfile
 
【如何深入理解零拷贝技术】不同的零拷贝技术应用于不同场景 , 下面依次进行sendfile、mmap的分析
mmap(内存映射) + write
在前面我们知道 , read() 系统调用的过程中 , CPU会把内核缓冲区的数据拷贝到用户的缓冲区里 , 于是为了减少这一步开销 , 我们可以用 mmap() 替换 read() 系统调用函数 , mmap的系统调用作用是将内核空间地址映射为用户空间地址映射 , 这样CPU就不用将数据从内核态拷贝到用户态了
如何深入理解零拷贝技术

文章插图
mmap内存映射+write
图中涉及3次数据拷贝(1次CPU拷贝、两次DMA拷贝) , 具体拷贝过程如下:
1、应用进程调用mmap后 , DMA会把磁盘数据拷贝到内核page cache中 , 然后操作系统的应用进程和内核空间的共享这个缓冲区
2、应用进程再调用 write , 操作系统直接将用户缓冲区的数据拷贝到 内核的socket 缓冲区中 , 这一切都发生在内核态 , 由 CPU 来搬运数据
3、最后 , 把内核的 socket 缓冲区里的数据 , 拷贝到网卡的缓冲区里 , 这个过程是由 DMA 搬运的 。
由此可见mmap的优势:
1、可以减少一次数据拷贝的过程 。
2、但这还不是最理想的零拷贝 , 因为把用户缓冲区的数据拷贝到内核socket 缓冲区里的工作仍然需要通过CPU完成 。
3、相对于传统数据传输 , mmap减少了一次CPU拷贝 , 上下文切换依然需要四次
sendfile
sendfile主要使用到了两个技术:
1、DMA技术
2、传递文件描述符代替数据拷贝
3、一次系统调用代替两次系统调用
下面依次讲解这三个技术的作用 。
1.利用 DMA 技术
sendfile 依赖于 DMA 技术 , 将四次 CPU 全程负责的拷贝与四次上下文切换减少到两次 , 如下图所示:
如何深入理解零拷贝技术

文章插图
sendfile利用DMA技术
DMA 负责磁盘到内核空间中的 Page cache(read buffer)的数据拷贝以及从内核空间中的 socket buffer 到网卡的数据拷贝 。
2.传递文件描述符代替数据拷贝
传递文件描述可以代替数据拷贝 , 这是由于两个原因:
 
  • page cache 以及 socket buffer 都在内核空间中;
  • 数据传输过程前后没有任何写操作;
 
如何深入理解零拷贝技术

文章插图
sendfile利用传递描述符
利用传递文件描述符代替内核中的数据拷贝
sendfile + SG-DMA技术传输文件 , 需要进行2次用户态和内核态的切换 , 2次数据拷贝(1次DMA拷贝 , 1次SG-DMA拷贝)
注意事项:只有网卡支持 SG-DMA(The Scatter-Gather Direct Memory Access)技术才可以通过传递文件描述符的方式避免内核空间内的一次 CPU 拷贝 。这意味着此优化取决于 linux 系统的物理网卡是否支持 , 这也就意味着硬件也要受限制(Linux 在内核 2.4 版本里引入了 DMA 的 scatter/gather – 分散/收集功能 , 只要确保 Linux 版本高于 2.4 即可) 。
3.一次系统调用代替两次系统调用
由于 sendfile 仅仅对应一次系统调用 , 而传统文件操作则需要使用 read 以及 write 两个系统调用 。
正因为如此 , sendfile 能够将用户态与内核态之间的上下文切换从 4 次讲到 2 次 。
如何深入理解零拷贝技术

文章插图
sendfile系统调用代替read/write


推荐阅读