IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄

一旦某个文件句柄就绪,就能通知应用程序进行相应的读写操作;

没有文件句柄就绪时会阻塞应用程序,交出cpu;

多路时指网络连接,复用指同一个线程

IO多路复用的三种实现方式

  • select
  • poll
  • epoll

 


 

服务器端采用单线程通过select / epoll等系统调用获取fd列表,

遍历有事件的fd进行accept / recv / send,使其能支持更多的并发连接请求 

IO多路复用用来解决什么问题?

当多个客户端与服务器通信时,若服务器阻塞在其中一个客户的read(sockfd1,…),

当另一个客户数据到达sockfd2时,服务器无法及时处理,此时需要用到IO多路复用。

即同时监听n个客户,当其中有一个发来消息时就从select的阻塞中返回,然后调用read读取收到消息的sockfd,然后又循环回select阻塞。

这样就解决了阻塞在一个消息而无法处理其它的。即用来解决对多个I/O监听时,一个I/O阻塞影响其他I/O的问题。


 select / poll / epoll之间的区别

  select poll epoll
数据结构 bitmap 数组 红黑树
最大连接数 1024 无上限 无上限
fd拷贝

每次调用

select拷贝

每次调用

poll拷贝

fd首次调用epoll_ctl拷贝,

每次调用epoll_wait不拷贝

工作效率 轮询:O(n) 轮询:O(n) 回调:O(1)

 

 

 

 

 

 

 

 

select缺点

单个进程所打开的FD是有限制的,通过FD_SETSIZE设置,默认1024

每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

对socket扫描时是线性扫描,采用轮询的方法,效率较低(高并发时)

select不是线程安全的

 

poll缺点(poll与select相比,只是没有fd的限制,其它基本一样)

每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

对socket扫描时是线性扫描,采用轮询的方法,效率较低(高并发时)

select和poll都会随着监控的文件描述符增加而出现性能下降,不适合高并发场景

 

epoll缺点

epoll只能工作在linux下,但是线程安全

 

epoll应用:Redis,Nginx

 

epoll LT (水平触发)与 ET(边缘触发)模式的区别

LT是默认的模式,ET是“高速”模式

LT模式下,只要这个fd还有数据可读,每次 epoll_wait都会返回它的事件,提醒用户程序去操作

ET模式下,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无论fd中是否还有数据可读。

所以在ET模式下,read一个fd的时候一定要把它的buffer读完,或者遇到EAGAIN错误

 

select的实现

将已连接的socket都放到一个文件描述符集合中,然后调用select函数讲文件描述符集合拷贝到内核中,

让内核检查是否有网络事件产生(遍历文件描述符集合),当检查到有事件产生后,讲此socket标记为可读或可写,

然后把整个文件描述符集合拷贝回用户态里,然后用户态通过遍历找到可读或可写的socket,然后再对其处理

select使用固定长度的BitsMap,表示文件描述符集合,在Linux中,内核中的FD_SETSIZE限制所支持的文件描述符个数,

默认最大值为1024,只能监听0~1023的文件描述符

 

poll的实现

 poll不在使用BitsMap表示文件描述符,而是使用动态数组,以链表的形式组织,突破了文件描述符个数的限制,

其他实现方式与select基本一样,这种方式会随着并发的数量,性能损耗呈指数级增长

 

epoll的实现

epoll通过两个方面解决了select和poll的问题

epoll在内核中使用红黑树来跟踪进程所有待检测的文件描述符,把需要监控的socket通过epoll_ctl()函数加入内核中的红黑树里,

红黑树是个高效的数据结构,增删查一般时间复杂度是O(logn),通过对这棵树的操作,就不需要想select/poll每次操作都传入整个socket集合,

只需传入一个待检测的socket,减少内核和用户空间大量数据的拷贝和内存分配

epoll使用事件驱动机制,内核里维护了一个链表来记录事件,当socket有事发生时,就会回调函数内核将其加入到这个就绪事件列表中,

当用户调用epoll_wait()函数时,只会返回有事件发生的文件描述符个数,不需要像select/poll那样轮询扫描整个socket集合,提高了检测的效率

即使监听的socket数量很多,也不会造成效率大幅度降低。同时监听的socket数目也非常多,以系统定义进程打开的最大文件描述符个数为上限