操作系统-mmap、sendFile、splice三种零拷贝技术介绍

转载文章

介绍

在传统网络数据传输的过程中,数据会被来回拷贝很多次,而其中有一些是不必要拷贝,而零拷贝技术就是为了减少这些不必要的数据复制操作。下面会详细介绍各种数据拷贝的详细过程,这也是一个非常高频的面试问题。

传统数据拷贝

当我们通过网络从服务器上获取数据时,数据整体的传输过程是这样子的,如图(可以放大看):

image-20241112111431751 详细过程:

  1. 等待cpu调度,通过cpu发起io请求,通过read()方法读取数据,此时用户态切换为内核态;
  2. DMA对硬盘发起IO请求;
  3. DMA从硬盘中把数据拷贝到pageCache中;
  4. DMA拷贝完成后发送完成信息;
  5. cpu从pageCache中把数据拷贝到用户缓冲区;
  6. 此时read()方法调用完成,内核态切换为用户态;
  7. 等待cpu调度,通过cpu发起io请求,通过write()方法写数据,此时用户态切换为内核态;
  8. cpu把数据从用户缓冲区拷贝到socket缓冲区;
  9. DMA通知网卡设备要发起IO请求;
  10. DMA开始进行数据拷贝;
  11. DMA拷贝完成,通知写完成信息;
  12. write()方法调用完成,内核态切换为用户态。

上述过程中出现了4次上下文切换,2次cpu拷贝,2次DMA拷贝;可以发现这里面有很多是不必要的操作,如:从pageCache拷贝到用户缓冲区,再从用户缓冲区拷贝到socket缓冲区;而零拷贝的出现就是为了节省这些cpu拷贝、上下文切换,从而提高服务的整体性能。目前linux提供了mmap、sendfile、splice等都是为了省去不必要的操作从而提升服务整体性能。

DMA拷贝

1
DMA其实就是在主板上安装了一块独立的芯片,它的作用当我们在内存和IO设备上传输设备的时候不需要cpu来控制数据的传输,而是直接通过DMA控制器来传输;这样就可以释放cpu来做其他的事情;但DMA只能用于设备之间的数据拷贝,所以传统数据传输只有在硬盘、网卡数据拷贝时才用得上。

mmap + write

mmap是linux内核提供的一种内存映射文件的方式,将一个进程的虚拟地址映射到磁盘文件地址。它可以将内核缓冲区的地址与用户缓冲区的地址进行映射,从而实现内核缓冲区到用户缓冲区的内存共享。省去数据从内核缓冲区拷贝到用户缓冲区的过程。具体过程如下:

image-20241112111538855

  1. 发起mmap()调用,建立用户缓冲区和pageCache的地址映射;
  2. DMA对硬盘发起IO请求;
  3. DMA从硬盘中把数据拷贝到pageCache中;
  4. DMA拷贝完成后发送完成信息;
  5. mmap()调用完成,内核态切换为用户态;
  6. 等待cpu调度,通过cpu发起io请求,通过write()方法写数据,此时用户态切换为内核态;
  7. cpu把数据从用户缓冲区拷贝到socket缓冲区;
  8. DMA通知网卡设备要发起IO请求;
  9. DMA开始进行数据拷贝;
  10. DMA拷贝完成,通知写完成信息;
  11. write()方法调用完成,内核态切换为用户态。

后续的write()方法和传统数据拷贝的过程是相同的,整体上发生了4次上下文切换,一次cpu拷贝,节省了一次从pageCache拷贝到用户缓冲区的cpu拷贝过程;同时用户态空间的共享区使用的是虚拟内存,并不会占用过多的物理内存。

优点:针对大文件可以极大的提高IO性能,但是对于小文件,内存映射反而会导致碎片空间的浪费。

sendfile

sendfile系统调用是Linux2.1引入的目的简化网络通过两个通道之间的数据传输;它可以使数据直接在内核空间进行IO传输,省去了用户空间和内核空间来回拷贝的过程。如图:

image-20241112111554791

  1. 发起sendfile()调用,用户态切换为内核态;
  2. DMA对硬盘发起IO请求;
  3. DMA从硬盘中把数据拷贝到pageCache中;
  4. DMA拷贝完成后发送完成信息;
  5. cpu从pageCache拷贝到socket缓冲区;
  6. DMA通知网卡设备要发起IO请求;
  7. DMA开始进行数据拷贝;
  8. DMA拷贝完成,通知写完成信息;
  9. sendfile()调用完成,内核态切换为用户态。

整个过程中只发生了2次上下文切换,1次cpu拷贝;相比mmap+write又节省了2次上下文切换。 而在linux2.4内核版本开始,又增加了gather操作,可以把仅有的这次cpu拷贝也省去掉。过程如下:

image-20241112111609402

上图红色字体的过程为改进点。它把数据描述信息读到socket的缓冲区中,DMA Gather Copy根据socket缓冲的数据描述信息批量的从pageCache中读取到网卡设备上。至此剩余的一次pageCache到socket缓冲的cpu拷贝也被节省掉了。

splice

上面的零拷贝,都是大家平时刷文章经常刷到的,这里再介绍一种不那么常见的splice。过程图如下:

image-20241112111622999

在第5步是创建一个splice管道,并且把该管道的写端绑定在pageCache上,读端绑定到socket缓冲区上,再通过DMA拷贝到网卡设备上,也实现真正意义上的零cpu拷贝。

作者:想打游戏的程序猿
链接:https://juejin.cn/post/7404036819107102739
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


操作系统-mmap、sendFile、splice三种零拷贝技术介绍
https://cason.work/2024/10/29/操作系统-mmap、sendFile、splice三种零拷贝技术介绍/
作者
Cason Mo
发布于
2024年10月29日
许可协议