零拷贝技术是指在计算机操做过程当中,CPU避免因为数据在内存间拷贝而浪费资源的技术。特别是在将文件在网络中输出的场景下,经过零拷贝技术能够节省不少的计算资源和内存资源。java
下图描述了传统的IO操做的流程。linux
![]() |
non-zerocopy |
不使用零拷贝技术,当文件输出到网络时,首先要将数据从内核缓冲区拷贝到程序缓冲区,而后再由程序将缓冲区拷贝的数据输出到内核缓冲区的网络输出的socketBuffer中;咱们能够看出拷贝到程序缓冲区这步其实是没有必要的。网络
引入了零拷贝,流程见下图:socket
数据将不会拷贝到程序缓冲区,而是由程序调用系统的零拷贝命令,数据将会从输入流直接被写入到输出流中,避免了一次无用的拷贝过程;性能
在linux中,能够经过mmap(), sendfile(), splice()实现零拷贝。spa
经过使用mmap接口替代read能够达到减小拷贝次数的目的。操作系统
tmp_buf = mmap(file, len); write(socket, tmp_buf, len);
使用mmap后,数据会经过DMA拷贝到操做系统内核缓冲区中,接着应用程序和操做系统共享这个缓冲区的数据,所以数据不须要再拷贝到应用程序缓冲区了。调用write后,操做系统将数据从内核缓冲区拷贝到与socket相关的内核缓冲区中,最后再拷贝到协议引擎中,一共三次数据拷贝;
使用 mmap 是 POSIX 兼容的,可是使用 mmap 并不必定能得到理想的数据传输性能。数据传输的过程当中仍然须要一次 CPU 拷贝操做,并且映射操做也是一个开销很大的虚拟存储操做,这种操做须要经过更改页表以及冲刷 TLB (使得 TLB 的内容无效)来维持存储的一致性。可是,由于映射一般适用于较大范围,因此对于相同长度的数据来讲,映射所带来的开销远远低于 CPU 拷贝所带来的开销。htm
Linux2.1引入sendfile技术,与mmap的区别主要在它不须要维持内核缓冲区数据到程序缓冲区的映射操做,所以它极大的减小了对存储的开销。可是它仍然有一次在操做系统内核缓冲区的数据拷贝过程,将数据拷贝到socket相关的缓冲区中。blog
sendfile() 系统调用利用 DMA 引擎将文件内容拷贝到内核缓冲区去;而后,将带有文件位置和长度信息的缓冲区描述符添加到 socket 缓冲区中去,此过程不须要将数据从操做系统内核缓冲区拷贝到 socket 缓冲区中,DMA 引擎会将数据直接从内核缓冲区拷贝到协议引擎中去,这样就避免了最后一次数据拷贝接口
Linux 2.6.17 内核引入了 splice() 系统调用,它和sendfile很是相似,可是它不须要指定输出一端是socket,任何的系统文件输出均可以。从这一点上讲,sendfile其实是splice的一个子集。
Java 类库经过 java.nio.channels.FileChannel 中的 transferTo() 方法来在 Linux 和 UNIX 系统上支持零拷贝。可使用 transferTo() 方法直接将字节从它被调用的通道上传输到另一个可写字节通道上,数据无需流经应用程序。
下面是一段将文件输出到httpServletResponse的代码:
//输出流 servletOutputStream = response.getOutputStream(); FileChannel channel = new FileInputStream(imgPath).getChannel(); response.setHeader("Content-Length", String.valueOf(channel == null ? 0 : channel.size())); channel.transferTo(0, channel.size(), Channels.newChannel(servletOutputStream));
经过零拷贝技术能够提升数据输出的时间延迟,下面是使用传统的IO输出和零拷贝输出的时间对比:
文件大小 | 正常文件传输(ms) | transferTo(ms) |
---|---|---|
7MB | 156 | 45 |
21MB | 337 | 128 |
63MB | 843 | 387 |
98MB | 1320 | 617 |
200MB | 2124 | 1150 |
350MB | 3631 | 1762 |
700MB | 13498 | 4422 |
1GB | 18399 | 8537 |