知识点整理(三)——零拷贝

背景

当用户通过浏览器从服务器上下载一个文件时,服务器具体是怎么处理这个文件的呢?
大致可以分为以下几步:

  1. 应用程序产生一个指定去读取需要的文件。
  2. 由于读取磁盘文件涉及到硬件,所以进程陷入内核态。
  3. 操作系统检查内核空间是否有文件缓存,有就直接把数据拷贝到用户空间。
  4. 如果没有,系统则通过磁盘读取数据,放入内核空间。
  5. 系统再将内核空间的数据复制到用户空间,最后切换会用户态,交由应用程序处理。
  6. 应用程序拿到数据后调用网络输出(输出流)。
  7. 由于网络输出涉及到硬件,所以进程陷入内核态。
  8. 由操作系统把应用程序的数据拷贝至内核空间(网络输出缓冲)。
  9. 由操作系统把内核空间的数据拷贝至网卡。
  10. 操作系统将进程切回用户态。

由此我们可以看到,这个文件数据一共经历了四次数据拷贝。数据拷贝需要CPU的参与(DMA处理除外),并且程序在用户态和内核态发生了多次上下文切换,增加了cpu负担。
如果使用DMA来处理硬件拷贝,那这种处理方法仍旧有2次数据拷贝使用到了CPU。

零拷贝 zero-copy

什么是零拷贝?就是避免cpu参与这种数据拷贝,让数据传输不经过用户空间。
零拷贝适用于不对数据进行修改、过滤等处理的场景,最常见的就是下载文件、透传数据等等。

mmap

使用mmap()来代替read调用能够减少一次拷贝。

mmap会把内核空间的数据映射一份出来到用户空间,这样就减少了一次拷贝。在数据量比较大的情况下会提升效率。

但是需要注意,在mmap映射后,文件被其他进程修改时,会产生一个错误,此时系统会发出SIGBUS信号。可以通过建立信号处理程序来处理这种情况,或者使用文件租借锁来避免。

sendfile

linux从2.1版本内核开始引入sendfile来简化操作。

  • 拥有DMA 收集拷贝的sendfile ,可以解决最后一次拷贝的问题。

这种收集拷贝功能是需要硬件以及驱动程序支持的

splice

sendfile只适用于将数据从文件拷贝到套接字上,限定了它的使用范围。

尾声

除此之外,还有一些零拷贝技术,比如传统的Linux I/O中加上O_DIRECT标记可以直接I/O,避免了自动缓存。