我看到epoll_wait()
和侦听器套接字之间的以下交互。创建侦听器套接字的事件顺序如下:
- call
socket()
call - 呼叫
fcntl()
,设置为非阻塞 - 呼叫
epoll_ctl()
与EPOLL_CTL_ADD
和EPOLLET | EPOLLONESHOT | EPOLLIN
- call
listen()
bind()
有后台线程为这个套接字和其他线程调用epoll_wait()
,如果一个线程在步骤4和5之间这样做,那么侦听器套接字将收到EPOLLHUP
事件。将序列更改为:
- call
socket()
- call
bind()
- 呼叫
fcntl()
,设为非阻塞 - call
listen()
- 呼叫
epoll_ctl()
与EPOLL_CTL_ADD
和EPOLLET | EPOLLONESHOT | EPOLLIN
解决了这个问题,但是现在我看到了虚假的失败,其中建立了连接,但是侦听器套接字没有接收到EPOLLIN
事件。
我理解可以选择使用电平触发模式,但我想让这个工作在边缘触发模式。
有什么想法吗?
EPOLLONESHOT
的语义是,一旦epoll_wait()
为某些描述符拉出通知,您将不得不使用EPOLL_CTL_MOD
调用epoll_ctl()
来重新启用该描述符上的通知。因此,您可能只是因为EPOLLONESHOT
在被动/侦听套接字上禁用了通知而丢失连接。(一般情况下,除特殊情况外,EPOLLONESHOT
不应使用;自动添加到代码中不是样板或巫术)
此外,使用非阻塞边缘触发语义,在通知侦听套接字已准备好输入时,需要在循环中调用accept()
,直到报告EAGAIN
错误。只调用一次accept()
可能会将其他连接留在队列中,而且,在将一个全新的连接添加到该队列之前,不会发生另一个边缘触发的EPOLLIN
事件。(也就是说,假设没有指定EPOLLONESHOT
,或者在下次调用epoll_wait()
之前每次都重新启用描述符。)
附录消除epollonshot当然值得一试。你到底为什么需要它?