zkcrescent 的木屋

Zero Copy

零拷贝

定义:指从内核空间到用户空间的拷贝次数为零

问题

计算机中,应用程序通常会遇到将磁盘数据写入网络的情况,会造成用户态和内核态 的频繁切换,具体过程如下:

  1. 应用程序将要发送某个磁盘上的文件,发起系统调用(1次 用户态到内核态切换)
  2. 内核态中将磁盘数据放入内核 buff (DMA (Direct Memeory Access))
  3. 再将内核 buff 放入应用程序 buff(2次 内核态到用户态) (CPU COPY)
  4. 程序请求发送网络,将应用程序 buff中的数据写入 socket buff(3次 用户态到内核态)(CPU COPY)
  5. 内核将socket buff 写入 nic(Network interafce controller) buff (DMA)
  6. 返回应用程序继续执行(4次 用户态到内核态)

从上看出,整个过程出现两次中断,四次上下文切换,很明显,第二三四五步骤可以合并的话就可以减少很多不必要开的开销

sendFile

从内核 2.1 开始,引入该方法,可以实现以下过程:

  1. 应用程序将要发送某个磁盘上的文件,发起系统调用(1次 用户态到内核态切换)
  2. 内核态将磁盘数据放入内核 buff (DMA)
  3. 将内核 buff 中的数据放入 socket buff (CPU COPY)
  4. 将soket buff 数据拷贝到 nic buff(DMA)
  5. 返回应用程序

从上可以看出,过程简化为一次中断,3次拷贝,两次上下文切换

scatter-gather sendFile

2.4 后的内核有了这个,可以只复制kernal buff少量元信息的基础上,直接将 kernal buff 复制到 nic buff 中,过程就变成了:

  1. 应用程序将要发送某个磁盘上的文件,发起系统调用(1次 用户态到内核态切换)
  2. 内核态将磁盘数据放入内核 buff (DMA)
  3. 将内核 buff 数据在内核 buff 中的位置(offset)和数据大小(size)追加到socket buff
  4. nic buff 通过 socket buff 中的offset 和 size 直接从内核 buff 中拷贝(DMA)
  5. 返回应用程序

两次DMA COPY,很快

其他相关

MMAP

内存映射文件,将文件映射到进程的地址空间,实现物理地址和进程空间地址的对应

进程可以向访问内存一样访问这个文件,而不需要通过read, write 当 kernal buff 和 app buff 共用这个文件时,可以实现零拷贝

优势

  • 多进程访问时节约内存空间
  • 数据在内核中,可以直接发送到网络,而应用程序无法修改数据(保护数据)

劣势

  • 当映射文件写入,用时被另一个进程切断,可能被总线错误信号SIGBUS中断,而这个信号意味着KILL PROCESS或者DUMP CORE,对服务来说不可接受
  • 访问多个小文件时,不如 send file 高效