零拷贝是指计算机执行IO操作时,CPU不需要将数据从一个存储区域复制到另一个存储区域,进而减少上下文切换以及CPU的拷贝时间。它是一种IO操作优化技术。
内核态:内核空间是提供进程调度、内存分配、连接硬件资源等功能,是被系统保护的空间。进程运行于内核空间就叫做内核态
用户态:用户空间就算提供给各个进程使用的空间,不具备访问内核空间资源的权限,要访问内核空间,需要通过系统调度来完成。进程运行于用户空间就叫做用户态
上下文:cpu寄存器是cpu内置的容量小、速度极快的内存。程序计数器是存储cpu正在执行的指令位置。他们都是cpu在运行任务前,必须准备好的依赖环境,因此(cpu寄存器的内容和程序计数器的内容)叫做cpu上下文。
上下文切换:先把上一个任务的cpu上下文(cpu寄存器的内容和程序计数器)保存起来,然后加载新任务的上下文到cpu寄存器和程序计数器上,再跳转到程序计数器所指向的位置继续执行任务(现在一般指的是内核态和用户态的切换)
DMA全称Direct Memory Access,即直接内存访问。
DMA本质是主板的一个独立芯片,允许外部设备和内存存储器进行直接的IO传输,其过程不需要CPU的参与。
也就是说DMA在工作时,cpu是空闲的,可以去干其他事情,从而提升整体的效率
虚拟地址取代物理地址,把硬盘当内存
优点:
零拷贝可以利用多个虚拟内存可以指向同一个物理地址这个特点,把内核空间和用户空间的虚拟地址映射到同一个物理地址,这样就会减少IO的数据拷贝次数
传统IO总共经历了4次上下文切换,4次拷贝(2次CPU拷贝和2次DMA拷贝)
零拷贝不是没有拷贝,而是减少用户态和内核态直接的切换以及cpu的拷贝次数
mmap用了虚拟内存的特点,将内核中的读缓冲区和用户空间的缓冲区进行映射,所有IO在内核中完成
mmap总共经历了4次上下文切换,3次拷贝(1次CPU拷贝和2次DMA拷贝)
sendfile是linux2.1内核引入的一个系统调用函数,表示两个文件描述符直接传输数据,在操作系统的内核中操作,避免数据在内核缓冲区和用户缓冲区之间的拷贝,实现零拷贝操作
sendfile总共经历了2次上下文切换以及3次拷贝(2次DMA拷贝+1次CPU拷贝)
linux2.4版本后,对sendfile进行优化,引入SG-DMA,运用scatter/gather操作,他可以直接从内核空间缓冲区将数据读取到网卡,省去cpu拷贝到socket缓冲区
sendfile+DMA scatter/gather总共经历了2次上下文切换以及2次拷贝(2次DMA拷贝)(真.零拷贝),全程没有cpu参与拷贝数据
实现方式有下面几种
1. mmap
2. sendfile
3. 升级版sendfile(DMA scatter/gather)
在java中,mmap最经典就是NIO用MappedByteBuffer进行实现内存映射
sendfile可以用FileChannel中得transferTo()或者transferFrom(),底层就是用sendfile(),kafka实现零拷贝也是用sendfile
kafka底层就是调用sendfile,拷贝就是零拷贝,所以快
答案是:不是
关键的技术是PageCache(磁盘高速缓存,内核缓冲区),PageCache的功能
如果大文件也用PageCache,那PageCache就会被大文件占据,导致热点小文件无法利用,反而降低性能。PageCache的功能也大受影响,所以大文件拷贝应该绕过PageCache。
应用程序直接访问磁盘,直接绕过PageCache(内核缓冲区),减少一次内核缓冲区到应用程序的复制
主要是解决进程阻塞问题,异步IO没有涉及PageCache
所以对于文件传输要分情况: