C 带边缘触发和oneshot的Epoll仅报告一次
我当前正在将从创建的SOCKFD添加到具有以下事件的epoll实例:C 带边缘触发和oneshot的Epoll仅报告一次,c,linux,epoll,C,Linux,Epoll,我当前正在将从创建的SOCKFD添加到具有以下事件的epoll实例: const int EVENTS = ( EPOLLET | EPOLLIN | EPOLLRDHUP | EPOLLONESHOT | EPOLLERR | EPOLLHUP); 触发事件后,我将其传递给处理程序线程,读取sockfd,然后使用相同的标志通过epoll\u ctl重新启用它。但是,我只收到一次EPOLLIN事件。此外,如果我在收到第一个事件后随时终止客户端,我
const int EVENTS = (
EPOLLET |
EPOLLIN |
EPOLLRDHUP |
EPOLLONESHOT |
EPOLLERR |
EPOLLHUP);
触发事件后,我将其传递给处理程序线程,读取sockfd,然后使用相同的标志通过epoll\u ctl
重新启用它。但是,我只收到一次EPOLLIN
事件。此外,如果我在收到第一个事件后随时终止客户端,我也不会收到挂断事件。通过阅读手册页,我认为我理解了EdgeTriggered和OneShot的正确方法
下面是我正在使用的流程的一些伪代码:
const int EVENTS = (
EPOLLET |
EPOLLIN |
EPOLLRDHUP |
EPOLLONESHOT |
EPOLLERR |
EPOLLHUP);
void event_loop()
{
struct epoll_event event;
struct epoll_event *events;
events = calloc(100, sizeof event);
while (1)
{
int x;
int num_events = epoll_wait(epfd, events, 100, -1);
for (x = 0; x < num_events; x++)
{
another_thread(fd);
}
}
}
void another_thread(int fd)
{
// Read stuff until EAGAIN
struct epoll_event event;
event.data.fd = fd;
event.events = EVENTS;
epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &event);
}
const int事件=(
埃波尔特|
埃波林|
埃波尔德胡普|
埃波洛内斯肖|
埃波尔|
EPOLLHUP);
无效事件_循环()
{
结构epoll_事件;
结构epoll_事件*事件;
events=calloc(100,事件大小);
而(1)
{
int x;
int num_events=epoll_wait(epfd,events,100,-1);
对于(x=0;x
当我执行EPOLL\u CTL\u MOD
操作时,我没有收到任何错误,但从未收到其他事件的通知。如果我在第一个事件之后让read循环保持在repeat状态,它将读取客户端发送的所有后续数据,因此我知道数据正在进入,并且fd仍然打开并工作
通过检查strace
,线程是从创建的,并且具有标记CLONE\u FILES
,因此所有线程共享相同的fd表
为从单独线程读取事件重新启用fd的正确方法是什么
但是,我只收到一次EPOLLIN事件。此外,如果我在收到第一个事件后随时终止客户端,我也不会收到挂断事件
epoll_ctl(2)的手册页上说:
epolonneshot(从Linux 2.6.2开始)
设置关联文件描述符的一次性行为。
这意味着,在使用
epoll_wait(2)关联的文件描述符在内部
已禁用,epoll不会报告任何其他事件
接口。用户必须使用epoll\u ctl\u MOD调用epoll\u ctl()
使用新的事件掩码重新排列文件描述符
在您的情况下,当您获得第一个事件时,epoll将禁用sockfd。
当您使用EPOLL\u CTL\u MOD
重新启用sockfd时,它将在重新注册后通知内核接收到的所有事件。因此,第一次通知和重新注册之间的任何事件都将丢失。这可能是不获取任何挂起事件或数据的原因
从事件中删除epolonneshot
将更正您的代码,最终您不需要重新启用sockfd
由于您使用的是
EPOLLET
,因此不会有任何性能问题。事实并非如此,我在客户端代码中有3-5秒的巨大延迟,只是为了测试这种情况,我看到在客户端尝试再次发送之前,fd已经准备好了日志消息。删除“一次性”标志无效:(您是否给客户端3-5秒的延迟时间来检查您是否收到“epolldhup”事件,或者只是两次发送之间的延迟?两次发送之间的延迟。由于EPOLLET,您将在注册事件的状态发生变化时收到通知。在EPOLLIN | EPOLLET情况下,您将在读取缓冲区中出现新数据时收到通知。当执行第一次发送时,epoll将为其返回一个事件,epoll_wait将成功返回,您的线程将读取数据。同时,主线程将再次转到epoll_wait并等待某个事件。3-5秒后,将执行第二次发送,epoll_wait将在socketfd上返回相同的读取事件。这就是为什么您将获取多个读取事件标记。我想你误读了这个问题,我只收到一个通知。MCVE会有很大帮助。例如,伪代码没有解释事件循环和处理程序线程之间的切换。这里有三个“移动部分”:EPOLLET(你试过没有此标记的代码吗?),epolonneshot和多线程。因此,诊断真正的问题相对来说是困难的。这个问题为您解决了吗?我正在构建类似的东西,我很好奇您是否能让它工作?我也很好奇,经过这么长时间。也许这就像在另一个线程()中切换操作顺序一样简单。
:执行在调用epoll\u ctl()
以重新激活边缘触发器后,“在EAGAIN之前读取内容”。否则存在争用条件:新数据可能会在从I/ORead()
操作返回的EAGAIN与调用epoll\u ctl()之间进入读取缓冲区
,这将丢失边缘触发器。@arayq2这是修复方法。基本上,在开始读取之前重置事件中的所有标志,以避免在读取时丢失事件中的数据。