epoll ET 笔记

虽然关于epoll的文章很多了,但我还是写一点最近学习epoll的笔记吧。
epoll是Linux下高性能IO通知机制,相对于其他Linux的IO机制,epoll支持LT(level-triggered,水平触发)和ET(edge-triggered,边缘触发),能监控fd(file descriptor,文件描述符)数量不受FD_SETSIZE限制(但最大能打开的fd还是会受到限制),并且在空闲连接数多的时候更高效,还支持ET模式。

关于LT和ET,在我的理解,LT是在可用状态时一直通知,直到状态变成不可用,ET是状态从不可用到可用时,通知一次并不再通知,直到状态再次变成不可用。所以LT更可靠,ET更高效。对fd来说,不可用就是fd不可读(读缓存区空)或不可写(写缓存区满),反之,当fd变得可读(读缓存区读入数据)或可写(写缓存区不满),fd状态就是可用的,然后当读缓存区被读完但没有新数据读入读缓存区,或写缓存区写满但写缓存区数据还没有输出,状态又变成了不可用。

关于epoll的更详细的信息和使用,我就不再多说了,其他人的博客写的都比我好,我也是一知半解,不过我推荐先去man epoll一下,也许有很大收获也说不定。

我觉得在使用epoll ET的时候可能需要注意几个问题:

  1. EAGAIN

    1
    在ET模式下,当一个fd可IO的时候,不代表只能一次IO调用。例如当一个监听socket可读的时候,不代表只能accpet一次,之前看过篇博客,作者就是遇到这样的问题,他认为是listen丢失了,然后通过epoll_ctl重置事件才解决,这就是我上面说的可用状态下如果需要再次收到通知的情况,当然其实更简单的做法是只要死循环直到accept返回-1并且errno为EAGAIN就行。读写调用也类似,如果errno是EAGAIN,需要等待事件通知后再次发送。
  2. events

    1
    首先在调用epoll_ctl添加fd进epoll时,需要指定监控的events掩码,而仅监控需要的事件,可以让效率更高。然后在ET模式中,如果监控的事件除非状态又从不可用到可用,否则不再通知,但在可用状态下如果需要再次收到通知,可以通过epoll_ctl修改fd监控的events掩码再次加入指定的事件,事件会被再次通知。

对于服务端的监听socket,我把events设置为EPOLLIN,因为监听socket在有新socket连入的时候产生的是EPOLLIN事件,所以需要设置EPOLLIN,另外由于监听socket一般不会出错误,如果出问题也是我们服务端出了问题。而对于远端socket,我则设置EPOLLIN和EPOLLRDHUP,监控数据的读入和远端关闭。对远端socket写数据,其实默认情况下是直接可写的(一般就绪后都是可写),保险起见还是在添加到epoll的时候直接events掩码加入EPOLLOUT,接收到了EPOLLOUT时,如果有数据待写,就一直写到write返回-1并且errno为EAGAIN,按需要修改events掩码加入EPOLLOUT,然后继续等待EPOLLOUT,如果数据写完或者没有数据可写了就移除EPOLLOUT(注意这时候远端socket还算是可写状态的)。其实也就是除非write发生EAGAIN,否则不监控EPOLLOUT。