epoll,select,poll的区别
注:看到一篇讲解epoll原理的文章,写得非常详细,建议阅读:epoll实现原理
之前分别记录了epoll(并发程序设计3:多路IO复用技术(2)),select和poll(并发程序设计2:多路IO复用技术(1))的用法,本节比较一下它们各自的特点。
1. select和poll的不同
(1) select和poll的原理和用法基本上是一样的,其内部实现机制也差不多,主要区别在于注册的结构体不一样。select采用fd_set结构体,每次文件描述符发生改变,相应的fd_set结构体就被改变了,因此在每次调用select之前要重置fd_set结构体。而poll采用的pollfd数组,结构为:
struct pollfd { int fd; short events; short revents; }
注册的事件和发生的事件分别用events和revents表示,所以只需置一次pollfd数组。简言之,select每次都要重置fd_set,poll只需要置一次pollfd结构体。
(2) poll可以注册的事件类型没有限制,而select注册事件有限制(FD_SETSIZE宏定义的值,32位机通常是1024,64位机通常是2048)。
2. select(poll)和epoll的区别
select和epoll实现机制不一样。下面的图说明了select的原理
epoll与select的主要不同就在于:
(1) epoll只在初始时完成一次文件描述符的注册,拷贝到内核,即在调用epoll_ctl()指定EPOLL_CTL_ADD时;
(2) epoll在内核态采用回调函数的形式,只有相应的文件描述符发生变化,回调函数才被调用,将发生变化的描述符集中到新的epoll_event的数组中,时间复杂度O(1);
(3) 返回的epoll_event数组只包含发生变换的文件描述符,所以不用遍历所有的文件描述符,时间复杂度O(1)。
详细的比较如下表:
系统调用 | select | poll | epoll |
事件集合 |
三个fd_set数组,分别记录可读,可写和异常事件, 内核修改fd_set数组来通知状态改变。每次调用之 前都要重置fd_set数组。 |
一个pollfd结构体数组,统一记 录所有的可读,可写及异常事件, 通过fdarray[i].events注 册事件,fdarray[i].revents通知事件改变 |
直接调用epoll_ctl将事件类型和描述 符添加到内核,只需要一次添加即可 |
应用程序索引就绪文件描述符的事件复杂度 | O(n) | O(n) | O(1) |
最大支持文件描述符数量 | 有限制 | 65535 | 65535 |
工作模式 | LT | LT | LT/ET(条件触发/边缘触发) |
内核实现方式及时间复杂度 | 轮询检测就绪事件O(n) | 轮询检测就绪事件O(n) | 回调函数形式检测就绪事件O(1) |
参考博客: