首先须要知道应用场景:
适用于静态资源从磁盘到网络的发送(中间不对资源进行改变),这在web server提供的功能中很常见,一个例子是:保存在磁盘上的一张图片应某个网络请求被从磁盘中取出并经过socket发送至请求方。linux
Linux系统分为user space和kernel space,user space中的不少操做须要经过系统调用陷入到内核中操做,好比文件读取操做。程序员经过某种编程语言提供的编程接口进行文件读取操做,好比C中的read,这个C语言的实现是经过调用kernel space提供的系统调用read实现的。在从user space陷入到kernel space的过程当中,须要进行contex switch。同理,在从kernel space到user space的过程当中也须要进行contex switch。程序员
网络端口发送数据的时候,有一块socket buffer做为要发送数据的缓存,而后驱动从其中读取数据并组包发送。web
对于上述的一张图片的请求和发送,从操做结果或者效果的角度看:我须要作的是将磁盘中的一部分数据直接(不加改变地)搬运到socket的buffer中进行组包再送到发送队列中等待发送的。能够想到:咱们从user space发起任务到回到user space,至少要经历两次contex switch,数据从磁盘到发送队列,至少也要通过两次copy(磁盘到socket buffer和socket buffer到发送队列)。编程
Zero在个人理解中是指在上述过程当中不存在多余copy的技术。缓存
怎么会存在多余的copy呢?多余的copy有什么坏处吗?直接按照上述的过程进行不是就可以作到没有多余copy了吗?
多余的copy应该是软硬件历史的缘由,这在后面的zero-copy的发展过程当中能够看到。多余的copy会占用CPU时间片和内存带宽,形成系统性能的下降。Zero-copy就是想作到上述的过程,即最直观的过程。须要说明的是,并非全部的数据的传输都适用zero-copy的,并且很大程度上不适用zero-copy的计算要比这种数据传输的应用要多得多,咱们必须意识到这中占大多数应用的存在,毕竟使用计算机作的事情并不只限于存储和传输。我的感受zero-copy是一种具体问题具体分析和精细化功能实现以换取性能的过程。网络
咱们来看看历史上对上述一张图片的响应的实现方式的变化:socket
实现1:contex switch 4次,copy 4次编程语言
read(file, tmp_buf, len); write(socket, tmp_buf, len);
上面是系统调用,下面是内存copy。这种方式没有什么可说的,是没有技巧性的中规中矩作法。函数
tmp_buf = mmap(file, len); write(socket, tmp_buf, len);
使用mmap在kernel space和user space之间构造了一块共享内存空间,至关于user space和kernel space都是访问统一块物理内存(可是在user space和kernel space中分别被映射到各自的虚拟地址空间中)。这种方式由于这块共享内存会产生问题:当另外一个进程操做同一个文件的时候,文件更改,kernel buffer中的内容至关于失效,此时,SIGBUS总线错误会终止以前的进程并产生core错误。解决的方法能够是设置信号的处理函数,可是这种方式可能会屏蔽掉真正发生错误的状况;或者是对操做的文件进行相似于加锁的操做(读写锁等)。或者若是真的想减小copy次数,只能让操做系统内核和硬件进行支持了,这就是后面的实现。性能
sendfile(socket, file, len); //since kernel 2.1
新的系统调用被添加到linux kernel中以支持文件间的copy操做,主要是不用在没必要要的时候bother user space。这种方式的好处是减小了contex switch的次数,可是实际上对于实现2中存在的另外一个进程修改使用的同一个文件的状况并不能很好地解决。
sendfile(socket, file, len); //since kernel 2.4
在这种方式中,socket适应实现3的方式,而且得到了硬件的相关的支持,在实现3中的第2步的copy在这种方式中只是copy了必要的描述信息:文件描述符和长度。相比于要copy的数据来讲,这大大减小了copy须要的时间。