相信你们应该对“零拷贝”这个词并不陌生了,这也算是大厂面试中的一个高频考点,玩过 NETTY 的朋友应该对此至关熟悉,NETTY 的高并发很大程度上都是由于 NIO,而 NIO 的核心就是零拷贝技术了,今天这篇文章就让你玩懂零拷贝。面试
传统的IO模型是怎么样的?
咱们先来看一张图,看看一个文件从磁盘传输到网卡究竟要经历什么样的磨难:网络
这种数据存储的区域总体将它叫作非直接缓冲区。并发
能够发现,竟然有四步数据拷贝的过程!!而且都是须要 CPU 去执行整个数据的传输过程的。异步
这时候就有个问题:这个过程也太繁琐了,我就想传输一些数据,干吗要传到用户这里,还要我本身再走一遍后续的流程,写到 socket 缓冲区再发出去,你不能帮我实现吗?socket
怎么去优化传统 IO 的流程?
再继续看上面的流程图理一下,看下能够去掉哪些步骤ide
能够看到在整个过程当中,从磁盘读出来到发送给网卡的数据,文件内容都是不会发生改变的,可是真正要将文件传输到网卡得经历4次文件内容的拷贝才行。函数
那么以最简单的的方式来讲,可不能够直接将磁盘中的数据传输到网卡呢?高并发
答案是固然不能够,缘由很简单,由于磁盘和网卡都是外部设备,因此必定须要有一个中间的缓冲区域来取存储数据,作一个转发的做用。性能
那么咱们再看看流程图,能作缓冲的有两个区域,一个是内核缓冲区,一个是socket缓冲区,用哪个?优化
这个问题应该很好选择了:只有用内核缓冲区来作缓冲区。socket 确定不能够了,由于socket 与我操做系统无瓜。
那么可不能够经过内核缓冲区直接给网卡发送数据呢?
看样子是能够的,咱们来看看,socket 缓冲区的做用是什么?
socket缓冲区的做用每一个 socket 被建立后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。
write()/send() 并非当即向网络中传输数据,而是先将数据写入缓冲区中,而后再由 TCP 协议将数据从缓冲区发送到目标机器上。一旦将数据写入到缓冲区,函数就能够成功返回,无论它们它们什么时候被发送到网络,也无论有没有到达目标机器,这些都是 TCP 协议负责的事情。
因此socket就是用来传输网络数据的,看来没它还不行。
可是咱们换个思路,是否是说,只须要告诉 socket 要传输哪些数据就能够了?而后文件内容就能够直接用内核缓冲区的就行。
零拷贝(zero copy)是怎么作到性能提高的当你能读懂上面的内容,基本上就已经能摸到零拷贝的核心脉络了,其实零拷贝就是使用内存映射来消除数据拷贝次数的,而后再使用 DMA 技术来减小CPU的工做时间。
就单从拷贝次数的性能来看,至少能够将性能提升百分之五十以上。
DMADMA在上文中常常提到这个很重要的词汇,它在整个零拷贝的流程当中占比是很大的,能帮助 CPU 作大量的工做,接下来咱们介绍一下这个神奇的技术。
DMA就是直接存储器访问,DMA (Direct Memory Access,直接存储器访问) 是全部现代电脑的重要特点,它容许不一样速度的硬件装置来沟通,而不须要依赖于 CPU 的大量中断负载。不然,CPU 须要历来源把每一片断的资料复制到暂存器,而后再次把它们写回到新的地方。在这个时间中,CPU 对于其余的工做来讲就没法使用。
原理:DMA 传输将数据从一个地址空间复制到另一个地址空间。当 CPU 初始化这个传输动做,传输动做自己是由 DMA 控制器来实行和完成。
零拷贝总体流程图
认真看到这里后,相信你应该对零拷贝已经有了深入的理解。那么 NIO 究竟是干什么的?既然说了一篇文章让你玩懂 零拷贝和NIO,那 NIO也必然不可少。
为何须要 NIO ?
全部的系统I/O都分为两个阶段:
须要注意的是:
等待就绪的阻塞是不使用CPU的,是在“空等”;而真正的读写操做的阻塞是使用CPU的,真正在”干活”,并且这个过程很是快,属于memory copy,带宽一般在1GB/s级别以上,能够将它理解为基本不耗时。
咱们先来看看传统IO是怎么作的
在传统的 socket IO中,须要为每一个链接建立一个线程。
一个线程对应一个链接,只处理一个链接的事情,这就是传统的socket IO。
当并发的链接数量很是巨大时,线程所占用的栈内存和CPU线程切换的开销就会很是大。
这种情境下还有可能会出现线程数量小于链接数量的状况,因此每一个线程进行 I O操做时就不能阻塞,若是阻塞,有些链接便得不处处理。
如上图,假如如今有三条线程在管理三条链接,而若是此时有第四个任务插入,那么就只能等待前面任务执行完成。
它的操做就像是一条流水线同样,是串行阻塞的,因此传统 IO 咱们也称为 BIO。
传统 IO 也不知道何时该处理数据,因此只能一直“傻等”。
而为了解决这些问题,NIO 就出现了。
NIO 是 怎么解决这些问题的?
咱们先来介绍一下 NIO 的核心组件:
通道有以下4个事件可供咱们监听:
首先咱们须要注册当这几个事件到来的时候所对应的处理器。而后在合适的时机告诉事件选择器:我对这个事件感兴趣。
也就是说,在选择器上注册了这四个事件的处理器,用来处理 channel 的事件,当 channel 某个事件真的准备就绪了,能够进行下一步的动做时,再告诉服务端来处理相应的数据,把相应的任务分配给服务端,这样就能更好的利用 cpu 的资源。
前面咱们说的零拷贝,就是在这时数据处理时发生的。
NIO 和 IO 有什么区别?从传统 IO 模型 到 NIO 零拷贝模型咱们能够看出,一个新技术的产生到崛起确定是由于它能知足以前技术知足不了的需求,或者性能有很高的提高。
传统 IO 传输须要进行四次的数据内容拷贝,包括用户态和内核态的切换,数据载体(磁盘、网卡)和内核态的切换,整个过程是阻塞的,浪费了不少资源。
而 NIO 是经过通道,选择器等核心模块,将整个 IO 处理过程变为异步的方式,只有其数据任务真正就绪了,才会让 cpu 去作处理,大量的提升了性能,亦节省了资源。
零拷贝是使用了内存映射,作到了内核态和用户态数据的零拷贝。而不是让内核态和用户态之间的数据再经过拷贝的方式传输。而由于使用了内存映射的关系,因此零拷贝技术没法对数据内容作更改。
它的拷贝方式使用了 DMA 技术,目的就是为了解决 CPU 拷贝数据的方式,让拷贝数据再也不占用 CPU 的资源,有 DMA 去完成。