在当前的网络流中,我们可以需要读网络io的两个过程
这就产生了一下的io模型:
当我们进行网络通讯的时候,首先要进行选择的就是确定是哪种IO模型。
当我们选择的时候要怎么选择呢?
于是我们就需要分析各个io的优缺点:
BIO | NIO | IO多路 | 信号驱动IO | 异步IO | |
---|---|---|---|---|---|
第一阶段 | 阻塞 | 非阻塞 | 阻塞 | 非阻塞 | 非阻塞 |
第二阶段 | 阻塞 | 阻塞 | 阻塞 | 阻塞 | 非阻塞 |
当我们讨论同步异步的时候,会分析是否需要等待任务完成才返回,需要等待任务完成才返回的是同步,不需要的是异步,而我们分析我们io的任务是把数据拷贝到用户空间,于是可以看到除了异步io,其他的都是同步的。
关于各种IO的详细介绍都放在后面了,那回到我们最初的问题,要怎样选择适合的IO模型呢?
我的想法是:
BIO:开发简单,在数据还没到内核时候阻塞,此时不会占用cpu资源,但一个线程一个socket,不适合高并发或者大量socket连接的场景。因为这样会创建大量的线程,线程资源有限。
NIO:基于轮询,于是就不会释放cpu,导致cpu的浪费,多并发下不可取,同时也是一个线程对应一个socket,线程资源也有限。
IO多路复用:也是轮询,但区别于NIO的轮询,是轮询多个socket,于是就可以一个线程配置多个socket,在高并发下需要创建的线程数量就比较少。但是如果是cpu繁忙型的,那么单纯使用IO多路复用是意义不大的,因为会被处理线程占满,没办法处理其他任务,这个时候可以开启线程池来处理,相比于NIO的轮询,BIO的完全一对一,是可以处于M:N,比如有M个socket的注册,而同时间只有N个socket比较活跃,使用BIO和NIO都是会一对一的线程,浪费线程资源。而IO多路复用就可以调用线程来处理活跃的IO。
AIO:是比较完美的模型,但线程一对一的问题也会是瓶颈,但现实是现在还没有存在。
非阻塞IO:
非阻塞IO区别于阻塞IO的在于第一阶段,非阻塞IO的两阶段过程:
IO多路复用在第一阶段时候也是阻塞的,但区别于阻塞IO,他处理的时候是轮询,如果没有任何一个监控的比如socket的信息拷贝到内核才阻塞。
和BIO比起来,IO多路复用要有一个轮询的过程,这个轮询的过程就是时间的瓶颈,但同时我们知道IO多路复用可以监听多个端口,一个线程可以处理多个socket,相对节省了线程资源。当线程在第一阶段阻塞后,如果有准备好的实践就会通知线程
现在使用的轮询模型有三种:select、poll、epoll,前两种实际上的处理还是需要把监听的fd传到内核空间去轮询是否已经准备好数据,于是这也需要大量的时间,而epoll直接在内核开辟一个空间,用来红黑树来存放监听的fd,数据传递好的fd就写入一个双向链表,这样只需要查看双向链表就可以判断是否可以进行第二步拷贝到用户空间,但第二步也是阻塞的。
信号驱动io的两步操作如下:
信号驱动io听起来和非阻塞io很像,主要是将主动轮询改变成了由系统通知,很像操作系统里面学的程序中断和IO中断
异步中断的两部分:
原文:https://www.cnblogs.com/waaaafool/p/14584427.html