select、poll、epoll的区别
事件集合
select:用户通过3个参数分别传入感兴趣的可读、可写及异常等事件,内核通过对这些参数的在线修改来反馈其中的就绪事件。这使得用户每次调用select都要重置这3个参数
poll:统一处理所有事件类型,因此只需要一个事件集参数,用户通过pollfd.events传入感兴趣的事件,内核通过修改pollfd.revents反馈其中就绪事件
epoll:内核通过一个事件表直接管理用户感兴趣的所有事件。因此,每次调用epoll_wait时,无须反复传入用户感兴趣的事件。epoll_wait系统调用的参数events仅用来反馈就绪的事件
应用程序索引就绪文件描述符的事件复杂度
select: O(n)
poll: O(n)
epoll: O(1)
最大支持文件描述符数
select: 一般有最大值限制
poll: 65535
epoll: 65535
工作模式
select: LT
poll: LT
epoll: 支持ET高校模式
内核实现和工作效率
select: 采用轮询方式来检测就绪事件,算法事件复杂度为O(n)
poll: 采用轮询方式来检测就绪事件,算法事件复杂度为O(n)
epoll: 采用回调方式来检测就绪事件,算法时间复杂度为O(1)
select、poll、epoll的区别补充
- 对于select和poll来说,所有文件描述符都是在用户态被加入其文件描述符集合的,每次调用都需要将整个集合拷贝到内核态;epoll则将整个文件描述符集合维护在内核态,每次添加文件描述符的时候都需要执行一个系统调用。系统调用的开销是很大的,而且在有很多短期活跃连接的情况下,epoll可能会慢于select和poll由于这些大量的系统调用开销。
- select使用线性表描述文件描述符集合,文件描述符有上限;poll使用链表来描述;epoll底层通过红黑树来描述,并且维护一个ready list,将事件表中已经就绪的事件添加到这里,在使用epoll_wait调用时,仅观察这个list中有没有数据即可。
- select和poll的最大开销来自内核判断是否有文件描述符就绪这一过程:每次执行select或poll调用时,它们会采用遍历的方式,遍历整个文件描述符集合去判断各个文件描述符是否有活动;epoll则不需要去以这种方式检查,当有活动产生时,会自动触发epoll回调函数通知epoll文件描述符,然后内核将这些就绪的文件描述符放到之前提到的ready list中等待epoll_wait调用后被处理。
- select和poll都只能工作在相对低效的LT模式下,而epoll同时支持LT和ET模式。
- 综上,当监测的fd数量较小,且各个fd都很活跃的情况下,建议使用select和poll;当监听的fd数量较多,且单位时间仅部分fd活跃的情况下,使用epoll会明显提升性能。
参考自《Linux高性能服务器编程》游双著