I/O复用
概述
1. I/O复用本质上是一种通知机制,用于通知某些事件的发生,常用的I/O复用机制有select、poll、epoll三种
2. I/O复用可以同时监听多个文件描述符,但是I/O复用本身是阻塞的
三种I/O复用的比较
1. 三种I/O复用都是通过某种结构体变量告诉内核需要监听哪些文件描述符上的哪些事件
(1)select使用fd_set,fd_set没有将文件描述符合需要监听的事件进行绑定,因此需要select需要提供3个该类型的参数来分别用来监听可读、可写以及异常事件,此外,内核直接对fd_set进行修改,因此下次使用select系统调用前需要进行重置fd_set
(2)poll使用pollfd结构体变量来告知内核,该结构体可以注册一个文件描述符上的多个事件,需要监听事件通过位或的方式注册在pollfd结构体中的events变量中,当内核监听到就绪事件后,会修改pollfd中的revents变量中,不会修改原有的注册事件,因此下次调用poll系统调用时无需像select那样进行重置
(3)epoll使用了一种完全不同的方式来管理文件描述符及其上注册的事件,它在内核事件中维护一个事件表,通过epoll_ctl来实现内核事件表的添加、删除和修改
(4)select、poll以及epoll_wait都会返回就绪的文件描述符的总数,但是epoll_wait会使用一个epoll_evnents数组来返回就绪的事件,因此只需要通过O(1)的复杂度就可以找到就绪的文件描述符,而select和poll需要通过O(n)的时间复杂度遍历整个文件描述符找到就绪的事件
2. poll和epoll_wait最多能监听的文件描述符的数目能够达到系统允许打开的最大文件描述符数据,而select能够监听的最大文件描述符的数目通常有限制
3. select和poll只支持低效的电平触发模式(LT模式),而epoll可以工作在高效地边沿触发模式(ET模式)
4. 在内核实现上,select和poll都是采用轮询的方式,每次都要扫描整个注册文件描述符集合,因此检测就绪事件的算法的时间复杂度O(n),epoll_wait采用回调的方式,内核检测到就绪的文件描述符时,将触发回调函数,该回调函数将该文件描述符上的就绪事件插入就绪事件队列中,内核在最后合适的时机将该就绪队列拷贝到用户空间,因此epoll不需要轮询整个文件描述符集合来检测就绪事件,时间复杂度O(1)