3

epoll 手册页说,如果没有进行读取,使用 EPOLLET(边缘触发)注册的 fd 不应通知两次 EPOLLIN。
因此,在 EPOLLIN 之后,您需要在 epoll_wait 能够在新数据上返回新的 EPOLLIN 之前清空缓冲区。

但是,我在使用这种方法时遇到了问题,因为我看到未触及的 fd 的重复 EPOLLIN 事件。
这是 strace 输出,0x200 是 EPOLLRDHUP,尚未在我的 glibc 头文件中定义,但在内核中定义。

30285 epoll_ctl(3, EPOLL_CTL_ADD, 9, {EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET|0x2000, {u32=9, u64=9}}) = 0
30285 epoll_wait(3, {{EPOLLIN, {u32=9, u64=9}}}, 10, -1) = 1
30285 epoll_wait(3, {{EPOLLIN, {u32=9, u64=9}}}, 10, -1) = 1
30285 epoll_wait(3,  <unfinished ...>
30349 epoll_ctl(3, EPOLL_CTL_DEL, 9, NULL) = 0
30306 recv(9, "7u\0\0\10\345\241\312\t\20\f\32\r\10\27\20\2\30\200\10 \31(C0\17\32\r\10\27\20\2\30"..., 20000, 0) = 20000
30349 epoll_ctl(3, EPOLL_CTL_DEL, 9, NULL) = -1 ENOENT (No such file or directory)
30305 recv(9, " \31(C0\17\32\r\10\27\20\2\30\200\10 \31(C0\17\32\r\10\27\20\2\30\200\10 \31("..., 20000, 0) = 10011

因此,在添加 fd 编号 9 之后,我在接收文件描述符之前确实收到了 2 个连续的 EPOLLIN 事件,系统调用跟踪显示了我如何在读取之前删除 fd,但它应该只发生一次,每个事件一个。
所以要么我没有正确阅读手册页,要么现在这里有什么工作。

4

3 回答 3

10

epoll我认为您错过了手册页的这一部分:

由于即使使用边缘触发的 epoll,在接收到多个数据块时也可以生成多个事件,调用者可以选择指定EPOLLONESHOT标志,告诉 epoll 在接收到带有epoll_wait(2). 指定标志后,调用者有责任使用withEPOLLONESHOT重新配置文件描述符 。epoll_ctl(2)EPOLL_CTL_MOD

也就是说:在第一次read()发生之前,你有两块数据到达你的接收队列,这意味着你有两个 epoll 事件。看起来这EPOLLONESHOT就是您所追求的,当事件发生时,它将从轮询集中原子地删除文件描述符(因此您不需要执行EPOLL_CTL_DEL)。

于 2010-04-20T10:02:33.763 回答
2

边缘触发只是意味着(除非您使用过 EPOLLONESHOT)当有东西进入(内核)缓冲区时,您将获得 1 个事件。

因此,如果您获得 1 个 EPOLLIN 事件并且对此不采取任何措施,那么下次有数据到达该描述符时,您将获得另一个 EPOLLIN - 如果没有新数据到达,您将不会收到事件,即使您没有读取第一个事件指示的任何数据。

于 2010-04-20T10:07:35.543 回答
-1

好吧,简而言之,EPOLLONESHOT 只是意味着如果您不读取您应该读取的数据,它们将被丢弃。

通常,如果您不阅读它们,您会收到相同数据的事件通知。然而,使用 EPOLLONESHOT,不读取数据是完全合法的,它们将被忽略。因此,不会生成更多事件。

于 2011-07-20T14:03:32.907 回答