简述
在做网络开发时,时常听到零拷贝、Reactor编程模型、JAVA NIO SocketChannel等的概念,还有一个优秀的网络编程构架,如Netty、Akka等,这篇Blog就“零拷贝”的概念及在Netty中的应用浅短讨论一下,文中不免有许多抄借和错误之处,请自行留意。
何为零拷贝?
操作系统中的零拷贝
一般地,用户程序进程工作在用户态的内存空间,而操作系统本身占用的内存属于系统太空间,因此如果我们想通过Socket通信进行收发数据时,由于用户程序产生的数据所处的内存空间与套接字Socket缓存区所处的空间不一样,这就需要CPU介入,将用户太空间的数据首先拷贝到系统空间的Socket缓存区中,然后才能由操作系统来读写这些数据,最终放入到网上的缓存区中如图1所示。这就存在了一次数据的拷贝,在大量数据需要传输的场景下,数据的拷贝无疑会占用更多 的CPU资源,导致整个系统的吞吐量的下降。
不幸的是,当请求的数据大于内核缓冲区大小时这种方法往往会成为性能瓶颈。数据在最终被发送之前,在磁盘,内核缓冲区和用户缓冲区之间发生多次拷贝。零拷贝通过减少不必要的数据拷贝以提供性能。

图1. 非零拷贝模式下的文件数据发送流程
正如你所看到的,上面的过程中存在很多的数据冗余。某些冗余可以被消除,以减少开销、提高性能。作为一名驱动程序开发人员,我的工作围绕着拥有先进特性的硬件展开。某些硬件支持完全绕开内存,将数据直接传送给其他设备的特性。这一特性消除了系统内存中的数据副本,因此是一种很好的选择,但并不是所有的硬件都支持。此外,来自于硬盘的数据必须重新打包(地址连续)才能用于网络传输,这也引入了某些复杂性。为了减少开销,我们可以从消除内核缓冲区与用户缓冲区之间的复制入手,如图2所示。
图2. 零拷贝模式下的文件数据发送流程
两种常见的实现”零拷贝“的方法:
方法一:sendfile(socket, file, len)
这个方法系统调用在内核版本2.1中被引入,以期在操作系统层实现文件数据的”零拷贝“。