零拷贝的问题

  • 零拷贝机制可以减少数据在内核缓冲区和用户进程缓冲区之间反复的 I/O 拷贝操作。
  • 零拷贝机制可以减少用户进程地址空间和内核地址空间之间因为上下文切换而带来的 CPU 开销。

传统的IO的操作

读操作

缓冲技术是IO的基础,一次读取大量数据放在缓冲区,需要的时候从缓冲区取得数据。

详细可见:内核缓冲区问题

title

一个完整的read操作:当应用程序发起read请求后,会检查内核空间内是否有需要读取的数据(pageCache),如果有,直接copy到用户空间;如果没有,那么需要从磁盘读取,磁盘控制器通过DMA操作将数据从磁盘读取到内核空间,然后才从内核空间拷贝到用户空间

DMA:不需要通过CPU调度,由DMA控制器来处理,不需要麻烦CPU。

读写操作

使用传统的I/O程序读取文件内容, 并写入到另一个文件(或Socket)。
性能开销比较大:

  • 上下文切换(context switch), 此处有4次用户态和内核态的切换
  • Buffer内存开销, 一个是应用程序buffer, 另一个是系统读取buffer以及socket buffer。
  • 需要进行四次拷贝,2次DMA copy和两次CPU copy。
    title
    title
    传统IO四次内容拷贝:
  • 先将文件内容从磁盘中拷贝到操作系统buffer
  • 再从操作系统buffer拷贝到程序应用buffer
  • 从程序buffer拷贝到socket buffer
  • 从socket buffer拷贝到协议引擎.

零拷贝

MMap

将物理内存映射到虚拟内存中。

在mmap之后,并没有在将文件内容加载到物理页上,只上在虚拟内存中分配了地址空间。当进程在访问这段地址时,若虚拟内存对应的page没有在物理内存中缓存,则产生”缺页”,将相应的页面载入物理内存

mmap()会返回一个指针ptr,它指向进程逻辑地址空间中的一个地址,这样以后,进程无需再调用read或write对文件进行读写,而只需要通过ptr就能够操作文件。但是ptr所指向的是一个逻辑地址,要操作其中的数据,必须通过MMU将逻辑地址转换成物理地址,若MMU没有相应的映射,产生缺页中断,将页面重新置入内存。

省去了从内核缓冲区复制到用户空间的过程,只有从磁盘调入到物理内存的过程。
title
它的最终目的是将磁盘中的文件映射到用户进程的虚拟地址空间,实现用户进程对文件的直接读写,减少了文件复制的开销,提高了用户的访问效率。
title

mmap+write
title
如何映射,见更多细节

sendFile

title
拷贝过程:

  • 首先通过DMA copy将数据从磁盘读取到kernel buffer中

  • 然后通过CPU copy将数据从kernel buffer copy到sokcet buffer中

  • 最终通过DMA copy将socket buffer中数据copy到网卡buffer中发送
    sendfile与read/write方式相比,少了一次复制,少了两次上下文切换。

    改进后的sendFile

    sendFile中间copy到socket buffer这一步仍是多余的。
    改进后的:
    title
    拷贝过程:

  • DMA copy将磁盘数据copy到kernel buffer中

  • 向socket buffer中追加当前要发送的数据在kernel buffer中的位置和偏移量

  • DMA gather copy根据socket buffer中的位置和偏移量直接将kernel buffer中的数据copy到网卡上。

改进后的只有两次复制了