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

时间:2010-04-20 09:16:54

标签: 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

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

3 个答案:

答案 0 :(得分:7)

我想你错过了epoll手册页的这一部分:

  

因为即使有边缘触发   可以生成epoll多个事件   收到多个大块的   数据,调用者可以选择   指定EPOLLONESHOT标志   告诉epoll禁用关联   收到后的文件描述符   epoll_wait(2)的活动。什么时候   指定了EPOLLONESHOT标志,   是来电者的责任   使用重新安装文件描述符   epoll_ctl(2)EPOLL_CTL_MOD

那就是:在第一次read()发生之前,你有两个数据块到达你的接收队列,这意味着你有两个epoll事件。似乎EPOLLONESHOT就是你所追求的,它会在事件发生时以原子方式从轮询集中删除文件描述符(因此你不需要执行EPOLL_CTL_DEL)。 / p>

答案 1 :(得分:2)

边缘触发只是意味着(除非你使用过EPOLLONESHOT)当某些东西进入(内核)缓冲区时你会得到1个事件。

因此,如果你得到1个EPOLLIN事件并且什么都不做,那么下次有些数据到达那个描述符时你会得到另一个EPOLLIN - 如果没有新数据到达,你就不会得到一个事件,即使你未读取第一个事件所示的任何数据。

答案 2 :(得分:0)

嗯,简单地说,EPOLLONESHOT只是意味着如果你不读取你应该阅读的数据,它们将被丢弃。

通常情况下,如果您没有阅读相同数据,则会收到有关事件的通知。但是,使用EPOLLONESHOT,不读取数据是完全合法的,它们将被忽略。因此,不会再生成任何事件。