1、我们说零拷贝,是从操作系统的角度来说的。因为内核缓冲区之间,没有数据是
重复的(只有 kernel buffer 有一份数据)。
2、零拷贝不仅仅带来更少的数据复制,还能带来其他的性能优势,例如更少的上下
文切换,更少的 CPU 缓存伪共享以及无 CPU 校验和计算。
(DMA--Direct memory access-直接内存拷贝)
①传统的io流是需要经历这样的过程:
用户态--DMA拷贝--内核态--CPU拷贝--用户Buffer--CPU拷贝--SocketBuffer--DMA拷贝--协议栈
经历了4次拷贝,4次状态切换
②后面出现了mmap:
mmap 通过内存映射,将文件映射到内核缓冲区,同时,用户空间可以共享内核
空间的数据。这样,在进行网络传输时,就可以减少内核空间到用户控件的拷贝
次数
用户态--DMA拷贝--内核态--改成直接读取--用户Buffer--CPU拷贝--SocketBuffer--DMA拷贝--协议栈
经历了3次拷贝,4次状态切换,但是还是有cpu拷贝
③Linux 2.1 版本 提供了sendFile 函数,其基本原理如下:数据根本不经过用户态,直接从内核缓冲区进入到 SocketBuffer,同时,由于和用户态完全无关,就减少了一次上下文切换
用户态--DMA拷贝--内核态--CPU拷贝--SocketBuffer--DMA拷贝--协议栈
经历了3次拷贝,3次状态切换,这里的CPU拷贝是极少的,就一些length,Offset的状态拷贝,接近于零拷贝了。
Linux 在 2.4 版本中,做了一些修改,避免了从内核缓冲区拷贝到 Sockebuffer 的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝。
用户态--DMA拷贝--内核态--CPU拷贝--DMA拷贝--协议栈
经历了3次拷贝,2次状态切换,这里的CPU拷贝是极少的,就一些length,Offset的状态拷贝,接近于零拷贝了。
这里总结一下mmap和sendFile的区别:
1) mmap 适合小数据量读写,sendFile 适合大文件传输。
2) mmap 需要 4 次上下文切换,3 次数据拷贝;sendFile 需要 3 次上下文切换,最
少 2 次数据拷贝。
3) sendFile 可以利用 DMA 方式,减少 CPU 拷贝,mmap 则不能(必须从内核拷贝
到 Socket 缓冲区)。
下面就来讲讲如何使用nio的transferto来实现零拷贝:
用户--DMA---channel----DMA---对方文件
linux中,使用transferto可以完成整个文件的传输,但是在window中,transferto一次只能发送8m的文件,就需要分片传输,注意传输起点(8*1024*1024)