1. 布景

最近在折腾网络编程,发现 IO 模型这块比较模糊,翻了不少材料,这儿总结分享下。 关键字:网络编程;IO模型

2. 前置常识一:内核态,用户态

想要弄懂 IO 模型,有一批前置常识需求掌握,首先是内核态和用户态的概念。 操作体系为了保护自己,设计了用户态、内核态两个状态。应用程序一般工作在用户态,当调用一些底层操作的时候(比如 IO 操作),就需求切换到内核态才可以进行。用户态和内核态的切换需求消耗一些资源,零仿制技术便是经过减少用户态和内核态的转换来提高功能的。

3. 前置常识二:应用程序从网络中接纳数据的大致流程

服务器从网络接纳的大致流程如下:

  1. 数据经过计算机网络来到了网卡
  2. 把网卡的数据读取到 socket 缓冲区
  3. 把 socket 缓冲区读取到用户缓冲区,之后应用程序就可以运用了

半小时搞懂 IO 模型
中心便是两次读取操作,五大 IO 模型的不同之处也就在于这两个读取操作怎样交互。

4. 前置常识三:了解同步/异步、堵塞/非堵塞

同步/异步:这个是应用层面的概念,指的是调用一个函数,我们是等这个函数履行完再继续履行下一步,仍是调完函数就继续履行下一步,另起一个线程去履行所调用的函数。重视的是线程间的协作。 堵塞/非堵塞,这个是硬件层面的概念,堵塞是指 cpu “被”歇息,处理其他进程去了,比如IO操作,而非堵塞则是 cpu 仍然会履行,不会切换到其他进程。重视的是CPU会不会“被”歇息,表现在应用层面就是线程会不会“被”挂起。 至于同步和堵塞有什么区别,异步和非堵塞有什么区别,其实这是不同层面的东西,欠好相互比较的。在学习IO模型的过程中,千万别钻这个牛角尖。

5. 前置常识四:了解同步堵塞、同步非堵塞、异步堵塞、异步非堵塞

有许多 IO 模型的博客,会把同步/异步、堵塞/非堵塞两两组合,把IO模型分成四类。

半小时搞懂 IO 模型
初看其实很疑惑的,都异步了,还咋堵塞啊?

其实大可不必纠结这个,同步/异步、堵塞/非堵塞自身便是不同层面的东西,强行组合起来便是欠好了解,甚至是错误的。

主张是抛开这个,直接去了解五大 IO 模型,千万别钻牛角尖。其实,真要分,也只能拆成两个维度分,而不是四个维度。 首先是按堵塞/非堵塞分:

半小时搞懂 IO 模型

然后是按同步/异步分:

半小时搞懂 IO 模型

6. 五大 IO 模型之:堵塞 IO

好了,假如掌握了前面提到的的这些前置常识,了解IO模型就稍微轻松点了,现在开端。

之前提了,应用程序从网络中接纳数据的大致流程便是两步:

  1. 数据预备:等候网络数据,把网卡的数据读取到 socket 缓冲区
  2. 数据仿制:把 socket 缓冲区的数据读取到用户态 Buffer,供应用程序运用

IO模型的不同之处也就在于这两个操作怎样交互,我们先看看堵塞IO模型

当应用程序建议 read 调用时,调用线程会堵塞住直到第一步读取操作的完结。 等第一步读取操作完结后,会将数据读取到用户态 Buffer 中,这个过程中调用线程仍然是堵塞的,直到数据仿制完结,整个流程用图来表明就张这样:

半小时搞懂 IO 模型

这种 IO 模型的优点便是好了解,API 简单好上手,适用于衔接数不多的网络应用。

7. 五大 IO 模型之:非堵塞 IO

当应用程序建议 read 调用时,假如没有数据可读,调用线程不会堵塞。但应用程序为了读到数据,就会一向循环调用,直到有数据可读。

等第一步读取操作完结后,第二步就和堵塞IO相同了。会将数据读取到用户态 Buffer 中,这个过程中调用线程仍然是堵塞的,直到数据仿制完结,整个流程用图来表明就张这样:

半小时搞懂 IO 模型

这种 IO 模型的并没有特别优点,并且会一向循环调用底层的接口,功能堪忧,很少运用。

8. 五大 IO 模型之:信号驱动 IO

当应用程序建议 read 调用,注册一个handler,等候有数据后的回调。应用程序一旦被回调,就阐明数据已经可以读取了,就会进行第二步操作,把数据读取到用户态 Buffer 中。同样,第二步仍然是堵塞的。

半小时搞懂 IO 模型

这种 IO 模型的优点便是相比于非堵塞IO,运用告诉&回调机制减少了循环的开支,可是对于衔接数多的场景,可能会由于信号行列溢出导致无法告诉,用的不多。

9. 五大 IO 模型之:多路复用 IO

当应用程序建议 read 调用时,假如没有数据可读,调用线程不会堵塞,体系会把 socket 注册到一个“多路复用器”上,等到有数据了会把可读的socket加入行列,供应用层运用。

半小时搞懂 IO 模型
大概的代码如下:

while (true) {
    if (selector.select(READ_KEY) > 0) { // selector 便是多路复用器,READ_KEY 大于 1 阐明有可读的socket
        Set<SelectionKey> set = clientSelector.selectedKeys();
        Iterator<SelectionKey> keyIterator = set.iterator();
        while (keyIterator.hasNext()) { 
            SelectionKey key = keyIterator.next();
            if (key.isReadable()) {
                // 读取数据
            }
        }
    }
}

这种 IO 模型的优点是可以应对很多的衔接,特别适用于很多的短衔接。现在大多数网络应用,底层选用的都是多路复用IO。

10. 五大 IO 模型之:异步 IO

异步IO 则和上面四种IO模型都不通,他是完完全全的异步,两步操作都不会堵塞。 应用程序建议 read 调用后,等收到回调告诉,就可以去运用用户态 Buffer 的数据了,如下图所示。

半小时搞懂 IO 模型

11. 打个比如

打个个人认为很贴切的比如,帮助了解。

半小时搞懂 IO 模型
我们都去医院取过药吧,五种IO模型就像是不同的取药方法。

  • 堵塞IO: 排队等药,排到我了可是药还没预备好,那我也继续等着,他人也不能取。这种我没好,他人也落不着好的方法,便是堵塞IO的体现。
  • 非堵塞IO: 排队等药,排到我了可是药还没预备好,那我从头排吧。从头排队便是轮询,由于从头排了也没有堵塞他人撤销,便是非堵塞IO的体现。
  • 信号驱动IO: 不必排队等药,药预备好了就直接短信告诉你去取。短信告诉就相当于信号驱动了,由于不必排队,节省了不少时刻。
  • 多路复用IO: 这个便是日常中常见的那种取药方法了,付了钱后要去药房的机器上扫码,然后盯着显示器,上面显示了你的姓名,再去取药。在机器上扫码就相当于注册,显示了你的姓名就相当于有需求处理的IO事情了。实际中显示了我的姓名,我仍是要去排队,这也是对应上的,由于一个 selector 返回的是多个需求处理的IO时刻,一个个处理就相当于一个个排队取药。
  • 异步IO: 这个就很赛博朋克了,异步IO就像是不必排队,不必取药,药好了直接寄你家,完全异步。

12. 可能会发生的疑问:

12.1 Java 的 nio 是对多路复用IO模型的完成,为什么叫非堵塞?

首先 Java 的 nio 包可以用来完成多路复用IO模型,也可以用来完成非堵塞IO模型,只不过非堵塞IO模型功能差没人用罢了。 其次,nio 中的那个“n”是 new 的意思。当时 JDK 的开发者为了和老的io包做区分,才用nio 来表明的,并不是 nonblocking 的“n”,所以叫“新IO包”更准确,也不容易弄混。

12.2 select、poll、epoll有什么关系

select、poll、epoll 都是用来完成多路复用的,原理也都是经过遍历找到可读写的socket,区别在于

  • select 有约束,最多1024个,poll、epoll没有这个约束。
  • poll 对数据结构有优化,没有 1024 个的约束,但仍是要遍历所有socket,目前很少用。
  • epoll 对遍历有优化,不会遍历所有socket,只会遍历那些可读的socket,所以效率有所提升。

12.3 信号驱动 IO 和多路复用 IO 很难分辩

信号驱动 IO 的底层机制是事情告诉,多路复用 IO 的底层机制是遍历+回调,只不过在应用层面包装成了事情罢了。

13. 总结

从网卡中读取数据有两步:第一步是网卡到 socket 缓存区,第二步是从 socket 缓冲区到内核态。

IO 模型有五种:堵塞IO、非堵塞IO、信号驱动IO、多路复用IO、异步IO。

  • 堵塞IO:两步都堵塞
  • 非堵塞IO:第一步不堵塞,但应用层不知道什么时候数据可读,所以需求不断轮询
  • 信号驱动IO:第一步不堵塞,但应用层不感知这一步的堵塞,机制是事情告诉机制,数据预备好后直接告诉应用层读取
  • 多路不必IO:第一步不堵塞,但应用层不感知这一步的堵塞,机制是遍历所有 Socket,有预备好的再告诉应用层读取
  • 异步IO:纯异步,数据预备好后,应用层直接运用。

都看到这了,给个赞吧

半小时搞懂 IO 模型

都看到这了,给个赞吧

半小时搞懂 IO 模型

都看到这了,给个赞吧

半小时搞懂 IO 模型

14. Ref

  • notes.shichao.io/unp/ch6/
  • 完全搞懂 select/poll/epoll,就这篇了!blog.csdn.net/Linuxhus/ar…
  • 图解四种 IO 模型 /post/704953…
  • 深入了解Java I/O模型 /post/684490…
  • 关于同步、异步与堵塞、非堵塞的了解 www.cnblogs.com/Anker/p/596…
  • Node的灵魂——非堵塞异步IO blog.csdn.net/chanmufeng/…
  • Java NIO–(1)高并发IO的底层原理及4种主要IO模型 /post/687371…
  • 计算机I/O与I/O模型 /post/684490…