一、什么是 epoll
epoll
是 Linux 内核为处理大批量文件描述符而作了改进的poll,是 Linux 下多路复用IO接口 select/poll
的增强版本,显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU的利用率。
它无需遍历整个被侦听的描述符集,只需遍历被那些内核IO事件异步唤醒而加入 Ready 队列的描述符集合就行了。epoll 除了提供了水平触发(Level Triggered)外,还支持边缘触发(Edge Triggered),用户空间程序可以缓存IO状态,减少 epoll_wait/epoll_pwait
的调用,提高程序效率。
二、epoll 的优点
2.1 支持打开大数目的 socket 描述符
Linux 默认打开的描述符是 1024,但是对于上万连接数的 IM 服务器来说这个数目太少了。虽然可以开多进程,但是进程间数据同步比不上线程间同步的高效,解决问题不够完美。但是 epoll 则没有这个限制,它所支持的 FD 上限是打开文件的数目,通过 cat /proc/sys/fs/file-max
可以查看。一般这个数目受系统内存影响。
2.2 IO效率不受描述符数量影响
若一个 socket 集合很大,由于网络延时,任一时间只有部分的 socket 是活跃的,传统的 select/poll 每次都会线性扫描全部集合,导致效率线性下降。而 epoll 不存在这个问题,它只会对活跃的 socket 进行操作(因为内核实现中epoll根据每个 fd 的callback函数实现的),即只有活跃的 socket 才会主动调用 callback 函数的情况。
注意
在一些 benchmark中,如果所有的socket基本上都是活跃的—比如一个高速LAN环境,epoll并不比select/poll有什么效率,相反,如果过多使用epoll_ctl,效率相比还有稍微的下降。但是一旦使用idle connections模拟WAN环境,epoll的效率就远在select/poll之上了。
三、工作方式
epoll 有两种工作方式:LT和ET
3.1 LT
LT(level triggered)是缺省工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表。
3.2 ET
ET (edge-triggered)是高速工作方式,只支持non-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。
ET和LT的区别就在这里体现,LT事件不会丢弃,而是只要读buffer里面有数据可以让用户读,则不断的通知你。而ET则只在事件发生之时通知。可以简单理解为LT是水平触发,而ET则为边缘触发。LT模式只要有事件未处理就会触发,而ET则只在高低电平变换时(即状态从1到0或者0到1)触发。