零拷贝总览
1)传统IO 需要4次复制(包括两次cpu复制) 4次用户态内核态的切换;
2)mmap/write 需要3次复制(包括一次cpu复制) 4次用户态内核态的切换;
3)sendfile 需要3次复制(包括一次cpu复制) 2次用户态内核态的切换;
4)linux2.4优化后,2次(只有两次DMA复制),2次切换,没有了cpu拷贝,实现了真正的零拷贝;
零拷贝中的零指的是cpu的零拷贝,允许DMA拷贝;
零拷贝可以分为操作系统层面和java层面;上面四点就是操作系统层面,java层面的实现可以理解为传递的是引用或是位置信息,而不是直接复制;
零拷贝产生背景
cpu内部有高速缓存,寄存器,运行速度非常快,内存条速度次之,网卡再次之,硬盘速度最慢。
cpu将数据发送至网卡过程
现在用户控件有100kb数据需要发送到对端,过程是怎样的呢?当没有DMA时,cpu会先从内存条中读取100k数据至其高速缓存,接着cpu会将100k写至网卡,这么一来会使cpu速度会拉低到和网卡一个速度,因为在读写过程中,cpu没办法再去执行其他进程了,所以DMA应运而生。
首先cpu会将100k数据从内存条读到其高速缓存,然后写至socket写缓冲区(内核空间的一块缓冲区),这里有一次cpu复制,接着cpu通知DMA控制器,让其将100k数据由socket写缓冲区移到网卡中,DMA收到命令后,会读取socket写缓冲区的数据至其本地缓存中,接着DMA将数据写至网卡中,直到socket写缓冲区的数据读完,并且数据全部由DMA缓存写至网卡后,DMA会给cpu发送一条DMA中断,告知cpu我做完了。为啥会有中断呢,原因分析如下:
若socket写缓冲区为50k,cpu执行当前进程A,从内存条中读100K数据的一半至其高速缓存,再将这50k数据复制至socket写缓冲区,此时socket写缓冲区已满了,当前进程A会从cpu的运行队列中被移至socket写缓冲器的等待队列中,即进程A变成了阻塞状态,接着cpu通知DMA控制器,让其将50k数据由socket写缓冲区移到网卡中,DMA收到命令后,先会读取socket写缓冲区的50k数据至其缓存中,接着DMA将50k数据写至网卡中,接着DMA会给cpu发送一条DMA中断,cpu收到中断后,会立马中断掉当前正在执行的进程B,将其从用户态切换至内核态,执行中断处理程序,中断程序逻辑是将socket写缓冲区的等待队列中的进程A移至cpu的运行队列中,即进程A变成了运行状态,中断处理程序结束,cpu会继续执行,所以进程A再次获得运行权利,cpu再次执行当前进程A,从内存条中读剩下50k数据至其高速缓存,再将这50k数据复制至socket写缓冲区,即重复以上操作。
cpu从本地硬盘读取数据的过程
cpu首先检查内核文件缓冲区中是否有硬盘中的数据,若有,则直接读,若没有,cpu直接取读硬盘,