Zero-Copy(零拷贝)
是指计算机执行操作时,CPU不需要先将数据从某处内存复制到另一个特定的区域,不需要将其从内核空间复制到用户空间切换以及消除传输数据之间不必要的中间拷贝次数,从而有效地提高数据传输效率。
利用零拷贝技术可以实现:
- 减少用户态与内核态(上下文)切换,上下文切换是一个很耗时的操作。
- 减少数据通过CPU拷贝,数据传输使用DMA(直接内存访问)技术,提高拷贝效率。
传统的数据读取/写入
从上图中,可以明显看到一次完整的数据读取和写入,会经历以下几个阶段:
- 发起读取数据时,首先会从用户空间切花到内核空间,再有内核空间直接向磁盘发起读取数据,再通过磁盘控制器 使用DMA拷贝
给到内核空间,当内核空间接受到数据后会将数据返回给用户空间。
此过程会经历一次CPU拷贝,一次DMA拷贝和两次用户态与内核态的切换。
- 获取到数据时,此时再将数据传输给对应的网关,需要经历由用户态切换到内核态,此过程伴随着CPU拷贝将数据传输到Socket缓冲区,再用DMA拷贝至对应的网关。
此过程也会经历一次CPU拷贝,一次DMA拷贝和两次用户态与内核态的切换。
虚拟内存
mmap/write方式
read()系统调用的过程中会把内核缓冲区的数据拷贝到用户的缓冲区里,为了减少这一步的开销,可以使用mmap()替换read()系统调用函数。
mmap()系统调用函数会直接把内核缓冲区里的数据映射到用户空间,这样,操作系统内核与用户空间就不需要再进行任何的数据拷贝操作。
此种零拷贝也并非是真正的零拷贝技术,与传统的相比,仅少了CPU拷贝,
上下文的切换并没有较少还是4次,还存在1次CPU拷贝和2次DMA拷贝
不同于传统的地方:将传统的read方法替换成mmap方法后,读取到数据之后,内核空间会将读取的数据共享shared给用户空间(通过创建数据内存映射来减少cpu拷贝),当用户空间在做写的操作时,会直接将内核缓冲区的数据根据映射关系直接读取Socket缓存区数据,从而减少了一次CPU拷贝。
sendFile
由于以上使用mmap()+write()并未真正解决用户态与内核态之间的切换以及CPU拷贝。
Linux内核在2.1版本:支持直接把内核缓冲区里的数据拷贝到socket缓冲区,不再拷贝到用户态,但还存在2次上下文切换和3次拷贝。
Linux内核在2.4版本:
sendFile()系统调用的过程发生了点的变化,MA控制器就可以直接将内核缓冲区中的数据拷贝到网卡缓冲区,此过程不需要将数据从操作系统内核缓冲区拷贝到Socket缓冲区中,而是将Read Buffer的内存地址、偏移量记录到相应的Socket缓冲区了 ,这样整个完整过程仅存在2次用户态与内核态的切换,两次DMA拷贝,不存在CPU拷贝。