select、poll、epoll三组I/O复用比较

一、事件集对比

select的参数类型fd_set没有将文件描述符和事件绑定,仅仅是一个文件描述符集合,因此需要提供3个这种类型的参数分别传入可读、可写及异常等事件。一方面使得select不能处理更多类型的事件,另一方面由于内核对fd_set集合的在线修改,下次调用select前不得不重置这3个fd_set集合。

poll的参数类型pollfd稍作改进,将文件描述符和事件都定义在其中,任何事件都被统一处理,使得编程接口简洁很多,而且内核每次修改的是pollfd结构体中的revents成员,而events成员保持不变,因此下次调用poll时应用程序无须重置pollfd中的事件集参数。

由于每次select和poll调用都会返回整个用户注册的事件集合(包括就绪的和未就绪的),所以应用程序索引就绪文件描述符的事件复杂度为O(n)。

epoll完全不同,在内核中维护一个事件表,并提供了一个独立的系统调用epoll_ctl来控制向表中添加、删除、修改事件。这样,每次epoll_wait调用都直接从该内核事件表中取得用户注册的事件,而无须反复从用户空间读入这些事件。epoll_wait系统调用的events参数仅用来返回就绪的事件,使得应用程序索引就绪文件描述符的时间复杂度达到O(1)。

二、最大支持文件描述符对比

poll和epoll_wait分别用nfds和maxevents参数指定最多监听多少个文件描述符和事件。这两个参数值都能达到系统允许打开的最大文件描述符数目,即65535(cat /proc/sys/fs/filemax)。

而select允许监听的最大文件描述符数量通常有限(好像是4095)。虽然用户可以修改这个限制,但这可能导致不可预期的后果。

三、工作模式对比

select和poll都只能工作在相对效率较低的LT模式,而epoll则可以工作在ET高效模式,而且epoll还支持EPOLLONESHOT事件。该事件能进一步减少可读、可写和异常等事件被触发的次数。

四、实现原理

select和poll采用的都是轮询的方式,即每次调用都需要扫描整个注册文件描述符集合,将其中就绪的文件描述符返回给用户程序,因此检测就绪事件的算法的时间复杂度为O(n)。

epoll_wait则不同,其采用回调方式,内核检测到就绪的文件描述符时,将触发回调函数,回调函数将该文件描述符上对应的时间插入内核就绪事件队列。内核最后在适当的时机将该就绪事件队列中的内容拷贝到用户空间,因此epoll_wait无线轮询整个文件描述符集合来检测哪些事件已经就绪,其算法的时间复杂度为O(1)。

但是,当活动的连接比较多的时候,epoll_wait的效率未必比select和poll高,因此此时回调函数被触发的过于频繁。所以epoll_wait适用于连接数量多,但活动连接较少的情况。

 

posted @ 2018-09-06 16:52  Juicyee  阅读(103)  评论(0)    收藏  举报