零拷贝总结(zero copy)

前言

在学习netty的过程当中,发现Netty对缓存作了不少优化,其中零拷贝一直令我迷惑,因此在网上找了一些博客进行学习,并做此总结css

定义

零复制(英语:Zero-copy;也译零拷贝)技术是指计算机执行操做时,CPU不须要先将数据从某处内存复制到另外一个特定区域。这种技术一般用于经过网络传输文件时节省CPU周期和内存带宽。 [1]

应用场景[2]

在web应用中,常常须要传输一些静态文件(css, js, 图片等),可讲这一过程抽象为下面两个系统调度:linux

read(file, tmp_buf, len);
write(socket, tmp_buf, len);

系统调用会进行上下文切换,共两次上下文切换:web

用户态 -> 内核态 -> 用户态

上面的传输过程当中,共涉及4次上下文切换,4次数据复制,其流程以下图
图片描述
上图中分为两部分,上半部分表示执行上下文(PC,寄存器等),下半部分表示数据区(堆栈等)
上班部分描绘了这一过程的上下文切换,下半部分描述了数据移动。缓存

在第一个区间里(处于用户态),执行read系统调度,读取文件数据到tmp_buf中,所以会进行上下文切换,切换到内核态(第二区间),而后内核从磁盘中读取文件数据到内核的缓冲区中(红线所示,这种复制是利用DMA直接从磁盘复制到内存,不通过CPU的寄存器,因此称为DMA copy),接下来在将文件数据从内核缓冲区中复制到用户缓冲区tmp_buf(蓝色虚线所示,这种复制是先从内存到寄存器,而后寄存器到内核缓冲区,因此称为CPU copy),并切换回用户态(第三个区间),此时read调用就完成了,文件数据存在tmp_buf中。安全

接下来进行write系统调用(处于第三个区间,用户态),讲tmp_buf的数据写入socket中,首先进行上下文切换,进入内核态(第四个区间),而后讲tmp_buf的数据复制到socket缓冲区中(蓝色虚线所示),最后利用DMA从socket缓冲区将数据复制到网卡,而后切换回用户态(第五个区间),此时wirte执行完成,可是数据可能尚未发送完成。网络

当须要进行大量的上述操做时,上下文切换和内存复制就会严重的影响性能,为此Linux内核提供了sendFile来解决这个文件。异步

为何read调用,须要先将数据从磁盘复制到内核缓冲区,而后再从kernel buffer复制到user buffer呢?

首先kernel buffer和user buffer大小不必定相等。
kernel buffer的做用是预读,每次调用read时不必定会触发磁盘读,只用当kernel buffer中的数据都被读取完后,才会触发,而后从磁盘中多读取一些数据到kernel buffer(从磁盘的读取数据的大小,并不彻底取决于用户执行的大小,还取决于kernel buffer的大小,以及文件的大小)。这样当用户屡次调用read读取同一文件的数据时,就不须要每次都直接从硬盘中读取,由于每次会多读一些数据到kernel buffer中,这样read就能够直接从kernel buffer中读取数据,而后复制到user buffer,这种作法在每次读取数据量小于kernel buffer的大小时,能够显著的提升效率,可是当每次读取数据量超过kernel buffer大小时,就会显得低效了。socket

为何write调用,须要先将数据从user buffer复制到kernel buffer,而后在从kernel buffer复制到网卡呢?

这样作的缘由是为了快速返回,从user buffer复制到kernel buffer的速度应该是快于从kernel buffer到网卡的,若是直接冲user buffer复制到网卡,会影响程序的性能。在将数据从user buffer复制到kernel buffer后,write就能够返回了,剩下的就是利用DMA从kernel buffer读取数据到网卡。
我认为,这是阻塞io的作法,对于一些异步io,是能够直接将user buffer的数据复制到网卡的(存疑,待之后解决)。性能

sendfile

Linux2.1

在Linux2.1中,sendfile的执行过程以下图:
图片描述
这里只有一次系统调用,因此只有两次上下文切换:用户态 -> 内核态 -> 用户态
数据复制有3次:磁盘 -> 内核缓冲区 -> socket缓冲区 -> 网卡
上述的复制种,从内核缓冲区到socket缓冲区显得比较多余,所以在Linux2.4对其进行了优化学习

Linux2.4

在Linux2.4种,sendfile的执行过程以下图:
图片描述
关键的改动在于,将kernel buffer的信息添加到socket buffer,而后协议引擎利用这个信息,直接从kernel buffer读取数据。可是这种操做,须要网卡支持gather操做。

问题

零拷贝,虽然对提高程序性能有很大的帮助,可是对传输的文件的安全性没有保证,至关于明文传输。
因为文件直接从磁盘读取到网络,因此没法对文件进行加密。所以对于须要对传输的数据进行加密的程序,如HTTPS,并不适用。

参考文献

[1] 零复制
[2] 什么是Zero-Copy?
[3] Zero-Copy&sendfile浅析
[4] Efficient data transfer through zero copy
[5] Zero Copy I: User-Mode Perspective

相关文章
相关标签/搜索