想成为大牛,不得不懂的五种Linux网络IO模型( 二 )


IO多路复用就是利用Linux的内核单线程去监听多个文件描述符,并在某个文件描述符可读、可写的时候接收到通知,避免无效的等待,充分利用CPU资源 。
关于监听的策略,在linux中提供了3种模式,也就是3个函数,分别是 select、poll、epoll,如下图所示 。

想成为大牛,不得不懂的五种Linux网络IO模型

文章插图
  • select?时间复杂度O(n),它仅仅知道了,有I/O事件发生了,却并不知道是哪几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作 。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长,而且最多支持的连接数量是1024个 。
  • poll?(翻译:轮询)时间复杂度O(n),poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,但是它没有最大连接数的限制,原因是它是基于链表来存储的 。
  • epoll?时间复杂度O(1),epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们 。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的 。(复杂度降低到了O(1)) 。
打个比方理解:
IO多路复用相当于找一个宿管大妈来帮你监视下楼的女生, 这个期间你可以些其他的事情. 例如可以顺便看看其他妹子,玩玩王者荣耀, 上个厕所等等 。IO复用又包括 select、poll、epoll 模式 。那么它们的区别是什么?
  • select?大妈 每一个女生下楼, select大妈都不知道这个是不是你的女神, 她需要一个一个询问, 并且select大妈能力还有限, 最多一次帮你监视1024个妹子 。
  • poll大妈不限制盯着女生的数量, 只要是经过宿舍楼门口的女生, 都会帮你去问是不是你女神 。
  • epoll大妈不限制盯着女生的数量, 并且也不需要一个一个去问. 那么如何做呢? epoll大妈会为每个进宿舍楼的女生脸上贴上一个大字条,上面写上女生自己的名字, 只要女生下楼了, epoll大妈就知道这个是不是你女神了, 然后大妈再通知你 。
通知你之后,你需要到女生宿舍门口,把女神带回自己宿色 。
优点:
  • 系统不必创建维护大量线程,只使用一个线程,一个选择器即可同事处理成千上万个连接,大大减少系统开销 。
缺点:
  • 本质上,它还是同步的,数据准备好后,拷贝阶段依然是阻塞的 。
如果处理的连接数不是很高的话,使用select/epoll的web server?不一定比使用mutil-threading + blocking IO的web server?性能更好,可能延迟还更大 。select/epoll 的优势并不是对于单个连接能处理得更好,而是在于性能更多的连接,比如Redis、Netty都是采用这种IO模型 。
4. 信号驱动IO模型当用户应用线程调用linux操作系统的sigaction函数,直接返回,然后该线程去做其他事情了,当有数据来了的时候,内核空间会去递交信号给用户空间,此时用户空间会调用recvfrom函数去将数据从内核空间缓冲区拷贝到用户空间缓冲区,并处理数据 。
想成为大牛,不得不懂的五种Linux网络IO模型

文章插图
打比方理解:
你给女神发短信,她下楼了主动通知你,你这时候在宿舍门口等着把她待会自己宿舍 。同比,进程无需等待内核数据准备结果,直接返回,事件信号通知进程结果,进程把数据copy到用户空间 。
优点:
  • 从一定意义上实现了异步,也就是数据的准备阶段是异步非阻塞执行的
缺点:
  • 当调用的线程过多,对应的信号量会增多,SIGIO函数处理不及时,会导致保存信号的队列溢出
  • 内核空间与用户空间频繁的进行信号量的交互,性能很差 。
现实中这种模式用的不多,就不展开阐述了 。纵观上述的所有IO模型:BIO、NIO、多路复用、信号驱动,本质上从内核缓冲区拷贝数据到程序缓冲区的过程都是阻塞的,如果想要做到真正意义上的异步非阻塞IO,那么就牵扯到了异步IO模型 。


推荐阅读