零拷贝介绍
零拷贝
零拷贝是指计算机执行IO操作时,CPU不需要将数据从一个存储区域复制到另一个存储区域,从而可以减少上下文切换以及CPU的拷贝时间。它是一种I/O操作优化技术。
传统 IO 的执行流程
传统的IO流程,包括read和write的过程。
- read:把数据从磁盘读取到内核缓冲区,再拷贝到用户缓冲区
- write:先把数据写入到socket缓冲区,最后写入网卡设备。
文件下载举例:
1、用户应用进程调用read函数,向操作系统发起IO调用,上下文从用户态转为内核态(切换1)
2、DMA控制器把数据从磁盘中,读取到内核缓冲区。
3、CPU把内核缓冲区数据,拷贝到用户应用缓冲区,上下文从内核态转为用户态(切换2),read函数返回
4、用户应用进程通过write函数,发起IO调用,上下文从用户态转为内核态(切换3)
5、CPU将用户缓冲区中的数据,拷贝到socket缓冲区
6、DMA控制器把数据从socket缓冲区,拷贝到网卡设备,上下文从内核态切换回用户态(切换4),write函数返回
传统IO的读写流程,包括了4次上下文切换(4次用户态和内核态的切换),4次数据拷贝(两次CPU拷贝以及两次的DMA拷贝)。
零拷贝实现方式:
零拷贝并不是没有拷贝数据,而是减少用户态/内核态的切换次数以及CPU拷贝的次数。零拷贝一般有这三种实现方式:
? mmap+write
? sendfile
? 带有DMA收集拷贝功能的sendfile
mmap+write
mmap就是用了虚拟内存这个特点,它将内核中的读缓冲区与用户空间的缓冲区进行映射,以减少数据拷贝次数!
1、用户进程通过mmap方法向操作系统内核发起IO调用,上下文从用户态切换为内核态。
2、CPU利用DMA控制器,把数据从硬盘中拷贝到内核缓冲区。
3、上下文从内核态切换回用户态,mmap方法返回。
4、用户进程通过write方法向操作系统内核发起IO调用,上下文从用户态切换为内核态。
5、CPU将内核缓冲区的数据拷贝到的socket缓冲区。
6、CPU利用DMA控制器,把数据从socket缓冲区拷贝到网卡,上下文从内核态切换回用户态,write调用返回。
mmap+write实现的零拷贝,I/O发生了4次用户空间与内核空间的上下文切换,以及3次数据拷贝(包括了2次DMA拷贝和1次CPU拷贝)。
sendfile
sendfile表示在两个文件描述符之间传输数据,它是在操作系统内核中操作的,避免了数据从内核缓冲区和用户缓冲区之间的拷贝操作。
1、用户进程发起sendfile系统调用,上下文(切换1)从用户态转向内核态
2、DMA控制器,把数据从硬盘中拷贝到内核缓冲区。
3、CPU将读缓冲区中数据拷贝到socket缓冲区
4、DMA控制器,异步把数据从socket缓冲区拷贝到网卡,
5、上下文(切换2)从内核态切换回用户态,sendfile调用返回。
sendfile实现的零拷贝,I/O发生了2次用户空间与内核空间的上下文切换,以及3次数据拷贝。其中3次数据拷贝中,包括了2次DMA拷贝和1次CPU拷贝。
带有DMA收集拷贝功能的sendfile
linux 2.4版本之后,对sendfile做了优化升级,引入SG-DMA技术,其实就是对DMA拷贝加入了scatter/gather操作,它可以直接从内核空间缓冲区中将数据读取到网卡。使用这个特点搞零拷贝,即还可以多省去一次CPU拷贝。
1、用户进程发起sendfile系统调用,上下文(切换1)从用户态转向内核态
2、DMA控制器,把数据从硬盘中拷贝到内核缓冲区。
3、CPU把内核缓冲区中的文件描述符信息(包括内核缓冲区的内存地址和偏移量)发送到socket缓冲区
4、DMA控制器根据文件描述符信息,直接把数据从内核缓冲区拷贝到网卡
5、上下文(切换2)从内核态切换回用户态,sendfile调用返回。
可以发现,sendfile+DMA scatter/gather实现的零拷贝,I/O发生了2次用户空间与内核空间的上下文切换,以及2次数据拷贝。其中2次数据拷贝都是包DMA拷贝。这就是真正的 零拷贝(Zero-copy) 技术,全程都没有通过CPU来搬运数据,所有的数据都是通过DMA来进行传输的。