EPOLLET模式下的Epoll在从套接字读取之前返回2个EPOLLIN

Ark*_*nez 2 linux networking epoll epollet

epoll联机帮助页说,如果没有读取,则EPOLLET注册的fd(边缘触发)不应通知两次EPOLLIN.
所以在EPOLLIN之后你需要清空缓冲区,然后epoll_wait才能在新数据上返回一个新的EPOLLIN.

但是我遇到了这种方法的问题,因为我看到了未触及的fds的重复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
Run Code Online (Sandbox Code Playgroud)

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

caf*_*caf 7

我想你错过了epollman page的这一部分:

由于即使使用边缘触发的epoll,也可以在收到多个数据块时生成多个事件,调用者可以选择指定EPOLLONESHOT标志,告诉epoll在收到事件后禁用相关的文件描述符epoll_wait(2).当EPOLLONESHOT指定的标志,这是调用者的责任使用重新武装文件描述符 epoll_ctl(2)EPOLL_CTL_MOD.

也就是说:在第一次read()发生之前,你有两个数据块到达你的接收队列,这意味着你有两个epoll事件.看起来就像EPOLLONESHOT你正在追求的那样,它会在事件发生时以原子方式从轮询集中删除文件描述符(所以你不需要这样做EPOLL_CTL_DEL).