Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/58.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ epoll\u等待返回EPOLLOUT,即使带有EPOLLET标志_C++_C_Linux_Networking_Epoll - Fatal编程技术网

C++ epoll\u等待返回EPOLLOUT,即使带有EPOLLET标志

C++ epoll\u等待返回EPOLLOUT,即使带有EPOLLET标志,c++,c,linux,networking,epoll,C++,C,Linux,Networking,Epoll,我正在边缘触发模式下使用LinuxEPoll。 每次传入新连接时,我都使用EPOLLIN | epolout | EPOLLET标志将文件描述符添加到epoll。我的第一个问题是:在epoll_wait返回后,检查每个就绪文件描述符发生何种事件的正确方法是什么?我的意思是,我看到一些示例代码,例如第124行中的代码,如下所示: for (int i = 0; i < n; i++) { //... if (events & (EPOLLIN | EPOLLERR))

我正在边缘触发模式下使用LinuxEPoll。 每次传入新连接时,我都使用EPOLLIN | epolout | EPOLLET标志将文件描述符添加到epoll。我的第一个问题是:在epoll_wait返回后,检查每个就绪文件描述符发生何种事件的正确方法是什么?我的意思是,我看到一些示例代码,例如第124行中的代码,如下所示:

for (int i = 0; i < n; i++) {
    //...
    if (events & (EPOLLIN | EPOLLERR)) {
        if (fd == lfd) {
            handleAccept(efd, fd);
        } else {
            handleRead(efd, fd);
        }
    } else if (events & EPOLLOUT) {
        if (output_log)
            printf("handling epollout\n");
        handleWrite(efd, fd);
    } else {
        exit_if(1, "unknown event");
    }
}
它似乎坚持了我的想法,一个接一个地检查所有的EPOLLERR、EPOLLIN、EPOLLOUT事件。 然后,我在应用程序中执行与nginx相同的操作。但我在实验后意识到:如果我使用EPOLLIN | epolout | EPOLLET标志将文件描述符添加到epoll中,并且我没有填充输出缓冲区,那么在epoll | u wait返回后,由于一些数据到达,并且这个fd变得可读,我总是会设置epolout标志,因此会调用冗余写入| u处理程序,这不是我所期望的

我做了一些搜索,发现这种情况确实存在,而不是由我的应用程序中的任何错误引起的。根据最受欢迎的投票结果显示:

有一点相关:如果您注册EPOLLIN和EPOLLOUT事件,并且假设从未填充发送缓冲区,那么每次触发EPOLLIN时,您仍然会在epoll_wait返回的事件中设置EPOLLOUT标志-有关更详细的说明,请参阅

答案中的链接是:

这并不意味着有一个ePloot“事件”,它只是意味着一条信息 被触发(由套接字变得可读)以便获得状态 更新。理论上,该程序不需要被告知ePloot 这里(应该假设套接字已经可写),但是 没什么害处

到目前为止,我对epoll边缘触发模式的理解是:

  • 当被监视的任何fd的状态发生变化时,例如从“无”变为“读取”->“可读”或“缓冲区已满”->“缓冲区可以写入”,epoll_等待返回

  • epoll_wait可能会为就绪列表中的每个fd返回一个或多个事件(标志)

  • sturct epoll_event.events字段中的标志指示此fd的当前状态。即使我们不填充输出缓冲区,当epoll_wait返回时也会设置EPOLLOUT标志,因为fd的当前状态是可写的

如果我错了,请纠正我。 然后我的问题是:我是否应该在每个连接中维护一个标志,以指示在写入输出缓冲区时是否发生EAGAIN,如果没有设置,请不要在“if(events&epolout)”分支中调用write_handler/handleWrite,这样我的上层程序就不会在这里被告知epolout了?

这是一个多么好的问题啊(因为我有几乎相同的问题)!我将总结一下我认为我现在知道的关于你的信息性问题/描述和你的有用链接,希望更聪明的人会纠正任何错误

是的,事件标志的if/else处理肯定是虚假的。可以肯定的是,至少有两个can事件可以有效地同时到达。例如,自从上次调用
epoll\u wait()
。并且,当然,只要
accept(),读和写端都可能已解锁
连接、读和写突然成为可能,因此您会得到一个“事件”
EPOLLIN | epolout

我真的没有想到,
epoll\u wait()
总是传递整个当前状态,而不仅仅是状态发生变化的部分——感谢您澄清了这一点。更清楚的是,
epoll\u wait()
将不会返回fd,除非该套接字上发生了更改,但如果发生了更改,它将返回表示当前状态的所有标志。因此,我发现自己盯着一个
EPOLLIN | epolout
事件流,想知道为什么它会声称存在“输出”你的回答是正确的:它只是告诉我输出端仍然是可写的

“我是否应该维护一个标志…”是的,但我可以想象,在除最琐碎的情况外的所有情况下,您可能最终都会为您的读者/作者维护至少一点“我当前是否被阻止”状态。例如,如果您希望以不同于数据到达方式的顺序处理数据(例如,将响应优先于请求,以使您的服务器更能抵抗过载)您必须立即放弃让I/O驱动一切的简单性。在编写的特定情况下,epoll根本没有足够的信息在“正确”位置通知您时间。一旦你接受一个连接,就会有一个事件说“你现在可以写了”——但是如果你是一个服务器,不可能已经收到客户端的请求,那么你可能没有什么要写的。epoll只是不知道你是否有要写的东西,所以你总是不得不要么写要么写“无关”事件,或保持自己的状态

除了最简单的情况外,Socket文件描述符最终是处理I/O事件的信息不足,所以如果您喜欢的话,总是必须将一些数据结构与它关联起来,或者对象。因此,我的C++看起来像:

nAwake = epoll_wait(epollFd, events, 100, milliseconds);
if(nAwake < 0)
    {
    perror("epoll_wait failed");
    assert(false);
    }
for(int iSocket=0; iSocket < nAwake; ++iSocket)
    {
    auto This = static_cast<Eventable*>(events[iSocket].data.ptr);
    auto eventFlags = events[iSocket].events;
    fprintf(stderr, "%s event on socket [%d] -> %s\n",
        This->ClassName(), This->fd, DumpEvent(eventFlags));

    This->Event(eventFlags);
    }
nAwake=epoll\u wait(epollFd,事件,100毫秒);
if(nAwake<0)
{
perror(“epoll_等待失败”);
断言(假);
}
对于(int-iSocket=0;iSocket%s上”\n,
This->ClassName(),This->fd,DumpEvent(eventFlags));
此->事件(eventFlags);
}
<> > <代码> Enviaby是一个C++类(或派生类),它具有决定如何处理标志EPOLL传递的所有状态。(当然,这是让内核存储指向C++对象的指针,需要一个VE的设计。
nAwake = epoll_wait(epollFd, events, 100, milliseconds);
if(nAwake < 0)
    {
    perror("epoll_wait failed");
    assert(false);
    }
for(int iSocket=0; iSocket < nAwake; ++iSocket)
    {
    auto This = static_cast<Eventable*>(events[iSocket].data.ptr);
    auto eventFlags = events[iSocket].events;
    fprintf(stderr, "%s event on socket [%d] -> %s\n",
        This->ClassName(), This->fd, DumpEvent(eventFlags));

    This->Event(eventFlags);
    }