EPOLLLET with Edge Trigger 可以比赛吗?

Can EPOLLLET with Edge Trigger race?

假设我的进程使用 epoll with Edge Trigger,并且以下 情景发生:

  1. 调用 epoll_wait,成功,一个 fd 准备好读取。
  2. recv()成功后,继续读取所有数据
  3. recv() return EWOULDBLOCK
  4. 现在有更多数据
  5. 转到第 1 步

epoll_wait() return 会马上吗?还是等到下一个数据进来?

马上就会return。

不幸的是,边缘触发模式没有得到很好的描述(无论如何,在我看来,不是以任何方式,所以普通人可以理解它并且不能做出错误的假设)。

它所做的是,当描述符的状态变为 "ready"(或者,无论您等待什么)时,它只生成一个事件,这意味着 epoll_wait() 将 return 恰好一次。然而,这并不是故事的结局。

一旦状态翻转到 "not ready" 并返回,它就会生成下一个事件。这一点非常重要。单独传入的数据通常是不够的(尽管根据文档可能,传入数据可能 生成不止一个事件... bleh)。

对于套接字或管道之类的任何东西,细微之处并不重要,因为无论如何您都会读取数据。这也很明显,如果你仔细想想,它也很有道理。

强调一次翻转到 false 并返回到 true 很重要,例如当您的描述符是 eventfd 时。假设您可以在需要唤醒服务员时只向 eventfd 发出信号(并且它不会多次唤醒等待线程,这很好)。当然,这就是它必须的样子,没有什么不同。也没有人关心你发的值,呵呵。。。只能存一个值,被覆盖了,我也懒得看。

好吧,这不是事情的运作方式,因为我很难找到。唤醒一次后,您需要实际读取描述符以将状态翻转为 "not ready" ,然后再等待。否则,令人惊讶的是,自从你上次醒来后又发生了另一个事件这一事实是无关紧要的。
同样,如果您考虑一下,这甚至是有道理的。只是不一定是你想的那样。

按照您的程序编写方式,它将按预期工作。但是请注意,仅给定一个描述符,仅阻塞会更有效。

这实际上在 epoll(7) manual page 中得到了回答(在 "Level-triggered and edge-triggered" 部分)。

手册上说它应该可以正常工作,因为 EPOLLET 事件是由 change 触发的,并且该更改发生在您的第 4 步中。

手册页甚至说使用EPOLLET解决问题的方法是

  1. with nonblocking file descriptors; and

  2. by waiting for an event only after read(2) or write(2) return EAGAIN.

这是您已经在做的事情(即使您使用等效的 EWOULDBLOCK 而不是 EAGAIN)。

简而言之:当您返回第 1 步时,epoll_wait 应该立即 return。