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,但它应该只发生一次,每个事件一个。
所以,要么我没有正确阅读联机帮助页,要么现在正在这里工作。
答案 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,不读取数据是完全合法的,它们将被忽略。因此,不会再生成任何事件。