零拷贝技术是一种思想,指的是计算机操作时,CPU不需要先将数据从某处内存复制从某处内存复制到另一个特定区域。可见,零拷贝的特点是 CPU 不全程负责内存中的数据写入其他组件,CPU 仅仅起到管理的作用。但注意,零拷贝不是不进行拷贝,而是 CPU 不再全程负责数据拷贝时的搬运工作。如果数据本身不在内存中,那么必须先通过某种方式拷贝到内存中(这个过程 CPU 可以不参与),因为数据只有在内存中,才能被转移,才能被 CPU 直接读取计算。
零拷贝技术的具体实现方式有很多,例如:
不同的零拷贝技术应用于不同场景,下面依次进行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.传递文件描述符代替数据拷贝
传递文件描述可以代替数据拷贝,这是由于两个原因:
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
总结下sendfile的具体过程如下:
此种方式对比之前的,真正意义上去除了CPU拷贝,CPU 可以去执行其他的业务计算任务,同时和 DMA 的 I/O 任务并行,极大地提升系统性能。
但他的劣势也很明显,强依赖于硬件的支持