2014年8月8日星期五

IO操作之零拷贝(ZeroCopy)

简介

零拷贝技术是指在计算机操作过程中,CPU避免由于数据在内存间拷贝而浪费资源的技术。特别是在将文件在网络中输出的场景下,通过零拷贝技术可以节省很多的计算资源和内存资源。
下图描述了传统的IO操作的流程。
non-zerocopy
不使用零拷贝技术,当文件输出到网络时,首先要将数据从内核缓冲区拷贝到程序缓冲区,然后再由程序将缓冲区拷贝的数据输出到内核缓冲区的网络输出的socketBuffer中;我们可以看出拷贝到程序缓冲区这步实际上是没有必要的。
引入了零拷贝,流程见下图:
数据将不会拷贝到程序缓冲区,而是由程序调用系统的零拷贝命令,数据将会从输入流直接被写入到输出流中,避免了一次无用的拷贝过程;

Linux中的零拷贝

在linux中,可以通过mmap(), sendfile(), splice()实现零拷贝。

mmap

通过使用mmap接口替代read可以达到减少拷贝次数的目的。
tmp_buf = mmap(file, len); 
write(socket, tmp_buf, len);
使用mmap后,数据会通过DMA拷贝到操作系统内核缓冲区中,接着应用程序和操作系统共享这个缓冲区的数据,因此数据不需要再拷贝到应用程序缓冲区了。调用write后,操作系统将数据从内核缓冲区拷贝到与socket相关的内核缓冲区中,最后再拷贝到协议引擎中,一共三次数据拷贝;
使用 mmap 是 POSIX 兼容的,但是使用 mmap 并不一定能获得理想的数据传输性能。数据传输的过程中仍然需要一次 CPU 拷贝操作,而且映射操作也是一个开销很大的虚拟存储操作,这种操作需要通过更改页表以及冲刷 TLB (使得 TLB 的内容无效)来维持存储的一致性。但是,因为映射通常适用于较大范围,所以对于相同长度的数据来说,映射所带来的开销远远低于 CPU 拷贝所带来的开销。

sendfile

Linux2.1引入sendfile技术,与mmap的区别主要在它不需要维持内核缓冲区数据到程序缓冲区的映射操作,因此它极大的减少了对存储的开销。但是它仍然有一次在操作系统内核缓冲区的数据拷贝过程,将数据拷贝到socket相关的缓冲区中。

带有DMA收集功能的sendfile

sendfile() 系统调用利用 DMA 引擎将文件内容拷贝到内核缓冲区去;然后,将带有文件位置和长度信息的缓冲区描述符添加到 socket 缓冲区中去,此过程不需要将数据从操作系统内核缓冲区拷贝到 socket 缓冲区中,DMA 引擎会将数据直接从内核缓冲区拷贝到协议引擎中去,这样就避免了最后一次数据拷贝

splice

Linux 2.6.17 内核引入了 splice() 系统调用,它和sendfile非常类似,但是它不需要指定输出一端是socket,任何的系统文件输出都可以。从这一点上讲,sendfile实际上是splice的一个子集。

java中的零拷贝

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)
7MB15645
21MB337128
63MB843387
98MB1320617
200MB21241150
350MB36311762
700MB134984422
1GB183998537
参考文档:



没有评论:

发表评论