EPOLLLET with Edge Trigger 可以比赛吗?
Can EPOLLLET with Edge Trigger race?
假设我的进程使用 epoll with Edge Trigger,并且以下
情景发生:
- 调用 epoll_wait,成功,一个 fd 准备好读取。
- recv()成功后,继续读取所有数据
- recv() return EWOULDBLOCK
- 现在有更多数据
- 转到第 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
解决问题的方法是
with nonblocking file descriptors; and
by waiting for an event only after read(2)
or write(2)
return EAGAIN
.
这是您已经在做的事情(即使您使用等效的 EWOULDBLOCK
而不是 EAGAIN
)。
简而言之:当您返回第 1 步时,epoll_wait
应该立即 return。
假设我的进程使用 epoll with Edge Trigger,并且以下 情景发生:
- 调用 epoll_wait,成功,一个 fd 准备好读取。
- recv()成功后,继续读取所有数据
- recv() return EWOULDBLOCK
- 现在有更多数据
- 转到第 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
解决问题的方法是
with nonblocking file descriptors; and
by waiting for an event only after
read(2)
orwrite(2)
returnEAGAIN
.
这是您已经在做的事情(即使您使用等效的 EWOULDBLOCK
而不是 EAGAIN
)。
简而言之:当您返回第 1 步时,epoll_wait
应该立即 return。