零拷贝技术

零拷贝

零拷贝(zero-copy)是一种I/O操作优化技术,可以快速高效地将数据从文件按系统移动到网络接口,而不需要将其从内核空间复制到用户空间,其在FTP或者HTTP等协议中可以显著地提升性能。但是需要注意的是,并不是所有的操作系统都支持这一特性,目前只有在使用NIO和Epoll传输时才可使用该特性。需要注意,他不能用于实现了数据加密或者压缩的文件系统上,只能传输文件的原始内容。这类原始内同也包括加密了的文件内容。

传统I/O操作

  1. 读操作

    1. 应用程序发起读数据操作,触发read()系统调用。这时操作系统会进行一次上下文切换(把用户空间切换到内核空间)。
    2. 通过磁盘控制器把数据复制到内核缓冲区(页缓存)中,这里发生了一次DMA Copy。
    3. 然后内核将数据复制到用户空间的应用缓冲区中,发生了一次CPU Copy。
    4. read调用返回后,会再进行一次上下文切换(把内核空间切换到用户空间)

    上述读过程,发生2次上下文切换和2次数据复制(一次是DMA Copy,一次是CPU Copy)。

    DMA Copy是内核从磁盘上面读取数据,这是不消耗CPU时间的,是通过磁盘控制器完成的。

  2. 写操作

    1. 应用程序发起写操作,触发write()系统调用,操作进行一次上下文切换(从用户空间到内核空间)
    2. 把数据复制到内核缓冲区Socket缓冲区,做了一次CPU Copy。
    3. 内核空间再把数据复制到磁盘或其他存储器(网卡,进行网络传输),进行了DMA Copy。
    4. 写入结束返回,又从内核空间切换到用户空间。

    上述写操作,也发生了2次上下文切换和2次数据复制(一次DMA Copy,一次CPU Copy)

image-20220608223134442

总结:

传统的I/O读写操作,总共进行了4次上下文的切换,4次的数据复制动作。数据在内核空间和应用空间之间来回复制,其实并没有做任何有意义的逻辑,就是单纯的复制而已。所以这个机制太浪费时间,而且浪费CPU时间。

零拷贝技术原理

零拷贝主要是用来解决操作系统在处理I/O操作时,频繁复制数据的问题。零拷贝技术主要有mmap+write、sendfile、splice等几种方式。

  1. 虚拟内存

    所有的现代操作系统都使用虚拟内存,使用虚拟地址取代物理地址,主要有以下几点好处:

    • 多个虚拟内存可以指向同一个物理地址
    • 虚拟内存空间可以远远大于物理内存空间

    利用上面的第一条特性可以优化,可以把内核空间和用户空间的虚拟地址映射到同一个物理地址,这样在I/O操作时 就不需要来回复制了。

    虚拟内存原理

    image-20220608224540237

  2. mmap/write方式

    使用mmap/write方式替代原来的传统I/O方式,就是利用了虚拟内存的特性。

    image-20220608225254819

    整体流程的核心区别是,把数据读取到内核缓冲区后,应用程序进行数据写入操作时,直接把内核的Read Buffer的数据复制到Socket Buffer以便写入,这次内核之间的复制也是需要CPU参与的。

    上述流程就少了一个CPU Copy,提升了I/O的速度。不过发现上下文的切换还是4次并没有减少,这是因为还是要应用程序发起write操作。

  3. sendfile方式

    从linux2.1版本开始,Linux引入了sendfile来简化操作。sendfile方式可以替换上面的mmap/write方式来进一步优化。

    sendfile将以下操作:

    1
    2
    mmap();
    write();

    替换为:

    1
    sendfile()

    这样就减少了上下文切换,因为少了一个应用程序发起write操作,直接发起sendfile操作。

    image-20220608230549337

    sendfile方式只有3次数据复制(其中只有一次CPU copy)以及2次上下文切换。

  4. 带有scatter/gather的sendfile方式

    Linux2.4内核进行了优化,提供了带有scatter/gather的sendfile操作,这个操作可以把最后一次CPU copy去除。其原理就是在内核空间Read Buffer和Socket Buffer不做数据复制,而是将Read Buffer的内存地址、偏移量记录到Socket Buffer中,这样就不需要复制,其本质和虚拟内存的解决方法思路一样,就是内存地址的记录。

    image-20220608231408062

    scatter/gatter的sendfile只有2次数据复制(都是DMA Copy)以及2次上下文切换。CPU copy已经完全没有。不过这一种收集复制功能是需要硬件及驱动程序支持的。

  5. splice方式

    splice调用和sendfile非常类似,用户应用程序必须拥有两个已经打开的文件描述符,一个表示输入设备,另外一个表示输出设备。与sendfile不同的是,splice允许任意两个文件之间互相连接,而并不是文件到socket进行数据传输。对于一个文件描述符发送数据到socket这种特例来说,一直都是使用sendfile系统调用,而splice一直依赖只是一种机制,它并不仅是sendfile的功能,也就是说,sendfile只是splice的一个子集。

总结:

无论是传统I/O方式,还是引入了零拷贝之后,2次DMA Copy是都少不了的。因为两次DMA都是依赖硬件完成的。所以,所谓零拷贝,都是为了减少CPU copy及减少了上下文的切换。

CPU拷贝 DMA拷贝 系统调用 上下文切换
传统方式 2 2 read/write 4
内存映射 1 2 mmap/write 4
sendfile 1 2 sendfile 2
sendfile with dma
scatter/gather copy
0 2 sendfile 2
splice 0 2 splice 2

零拷贝技术
http://example.com/2022/09/04/零拷贝/
作者
liziyuan
发布于
2022年9月4日
许可协议