网络IO的一些理解
网络IO
在当前的网络流中,我们可以需要读网络io的两个过程
- 等待数据传输到网卡,拷贝到内核缓冲区
- 将数据从内核缓冲区拷贝到用户空间来进行执行
这就产生了一下的io模型:
- 阻塞IO Blocking IO
- 非阻塞IO NonBlocking IO
- IO多路复用 IO Multiplexing
- 信号驱动IO Signal driven IO
- 异步IO Asynchronous 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:是比较完美的模型,但线程一对一的问题也会是瓶颈,但现实是现在还没有存在。
一、BIO:
- 在第一个阶段,发动系统调用去获取数据,但是比如网络数据还没到,就需要阻塞等待网络数据到达,然后拷贝到内核空间
- 第二阶段将数据从内核拷贝到用户空间,这也是得阻塞的,只有当完成后,用户才能访问用户空间进行处理
二、NIO
非阻塞IO:
非阻塞IO区别于阻塞IO的在于第一阶段,非阻塞IO的两阶段过程:
- 使用读系统调用去读数据,但是比如无数据报准备好,比如网络数据还没到达,回复一个没达到消息,就转去执行其他的。之后按照一定时间间隔来再次使用系统调用去读数据,直到数据拷贝到内核空间。
- 当内核空间已经有数据后,又在再一次读系统调用之后,将数据拷贝到用户空间,此过程需要阻塞
三、IO多路复用
IO多路复用在第一阶段时候也是阻塞的,但区别于阻塞IO,他处理的时候是轮询,如果没有任何一个监控的比如socket的信息拷贝到内核才阻塞。
- 第一阶段:发送轮询相关系统调用,查询所有所有注册监听的socket是否信息已经拷贝到内核,没有就阻塞,有任意一个就返回。
- 第二阶段:若第一阶段阻塞完成后发送有满足的返回后,调用读系统调用,从内核空间拷贝到用户空间,也得阻塞。
和BIO比起来,IO多路复用要有一个轮询的过程,这个轮询的过程就是时间的瓶颈,但同时我们知道IO多路复用可以监听多个端口,一个线程可以处理多个socket,相对节省了线程资源。当线程在第一阶段阻塞后,如果有准备好的实践就会通知线程
现在使用的轮询模型有三种:select、poll、epoll,前两种实际上的处理还是需要把监听的fd传到内核空间去轮询是否已经准备好数据,于是这也需要大量的时间,而epoll直接在内核开辟一个空间,用来红黑树来存放监听的fd,数据传递好的fd就写入一个双向链表,这样只需要查看双向链表就可以判断是否可以进行第二步拷贝到用户空间,但第二步也是阻塞的。
四、信号驱动IO
信号驱动io的两步操作如下:
- 第一步:发送信号的系统调用,如果数据还没有拷贝到内核,那么就不阻塞继续执行
- 第二步:等到有内核发消息说数据在内核准备好了,就调用读系统调用阻塞拷贝到用户空间
信号驱动io听起来和非阻塞io很像,主要是将主动轮询改变成了由系统通知,很像操作系统里面学的程序中断和IO中断
五、AIO
异步中断的两部分:
- 第一步:发送aio读的系统调用后,立马返回。不阻塞,去做其他事情。
- 第二步:剩下的等到数据数据到内核之后,由操作系统控制数据拷贝到用户空间,然后通知用户。用户直接使用就好。