北漂IT民工 的博客

Epoll 水平触发和边缘触发

下面是对man epoll的英文的部分翻译。


水平触发和边缘触发(Level-Triggered and Edge-Triggered)


epoll事件分发接口能在边缘触发(ET)或者水平触发(LT)两种模式下工作。他们的区别如下所述。


假设有这样一个场景:

1. 表示管道读取端的文件描述符(rfd)在epoll上注册成功

2. 在管道上写入了2Kb的数据

3. epoll_wait(2)调用完成,返回rfd表示这个描述符已经准备好

4. 从rfd上读取1kb数据

5. 调用epoll_wait(2)


如果rfd是使用EPOLLET(边缘触发)标记注册的epoll接口

那么在第5步调用epoll_wait(2)时可能会被挂起,

尽管数据仍可从文件输入缓冲取到,远程主机也在等待它的回应。

但是因为边缘触发模式只派发所管辖的文件描述符上的变化事件,

所以可能会导致第5步的调用者一直等待已经在输入缓冲的数据。


(注,原因应该是这样,由于没有读取完所有的数据,也没有写数据,所以两边都在等,因为新的事件就一直不能产生。)


在上面的例子里,一个rfd上的事件可能在第2步写入完成后产生,然后在第3步被消耗掉。

由于第4步的读取操作没有处理完所有的缓存数据,第5步的epoll_wait调用可能会被无限的阻塞。


一个使用EPOLLET标记的应用应该使用非阻塞的文件描述符以避免读/写的阻塞引起处理多个文件描述符的任务的饥饿。

作为边缘触发接口时,epoll建议的使用方式如下:

i 使用非阻塞文件描述符,

ii 只在read(2)或者write(2)返回EAGAIN后才等待一个事件。


相反,当用作水平触发接口时(默认方式,如果EPOLLET没有指定),epoll仅仅是一个更快的poll(2),并且可以用于任何poll(2)使用的地方,因为他们的语义是一样的。


但是即使使用边缘触发的epoll,接收到多块数据时还是会产生多个事件。

调用者这时可以使用EPOLLONESHOT标记,

它告诉epoll在用epoll_wait(2)接到一个事件后禁用关联的文件描述符.

如果EPOLLONESHOT标记指定后,

那么怎样使用epoll_ctl(2)和EPOLL_CTL_MOD来调整文件描述符就成了调用者的责任。