【问题标题】:epoll_wait seems to get stuck on EPOLLRDHUPepoll_wait 似乎卡在 EPOLLRDHUP
【发布时间】:2017-10-13 14:59:04
【问题描述】:

所以我有一些看起来像这样的代码:

for (;;) {
        errno=0;
        epoll_event e = {};
        auto wait_r = epoll_wait(g.epoll_fd, &e, 1, 0);
        if (wait_r==0) break;
        if(wait_r ==-1 && errno==EINTR) {
            printf("got EINTR\n");
            continue;
        }
        assert(wait_r == 1);

        auto& c = *(Context*)e.data.ptr;

        if(e.events & EPOLLERR ) {

            int       error = 0;
            socklen_t errlen = sizeof(error);
            auto r1 =getsockopt(c.socket, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen);
            assert(r1==0);
            printf("Got EPOLLERR 2 %s\n", strerror(error));
        }


        if(e.events & EPOLLRDHUP || e.events & EPOLLHUP ) {
            if (e.events & EPOLLRDHUP) {
                printf("got to EPOLLRDHUP\n");
            }
            if (e.events & EPOLLHUP) {
                printf("got to EPOLLHUP\n");
            }
            //continue; // keeps hitting this for same connections
            break;
        }

        if (e.events & EPOLLIN) {

            // Does a bunch of reads...
        }
    }
}

单个套接字将卡在EPOLLRDHUP || EPOLLHUP 案例中。套接字可能已关闭,当我尝试关闭它或执行 EPOLL_CTL_DEL 时,我得到一个 EBADFD。我的理解是 epoll 会自动摆脱任何死套接字,但情况似乎并非如此......有什么想法吗?

另一个可能的问题是,在套接字上,我正在使用 recvmsg/sendmsg,并且我正在通过这些套接字(它们是 unix 域流套接字)在进程之间发送文件描述符。我试图对其进行最终的 recvmsg,但也失败了......有什么想法吗?

【问题讨论】:

  • 我建议您的代码在处理完EPOLLHUP 事件之前调用close。请注意,EPOLLHUP 表示对方关闭了连接 - 但您的代码尚未关闭连接。由于fd 仍然在您身边打开,epoll 将继续轮询它。
  • @myst 当我尝试关闭它时,它会因文件描述符错误而失败,在这种情况下。但是感谢您对一般情况的建议。
  • 我确定客户端fd(不是g.epoll_fd)必须被您的代码关闭才能释放它的资源。您的代码不是一个完整的示例,因此我可以查看的数量是有限的。我不确定哪个函数调用会导致EBADFD 或您传递给close 的值。实际上,我什至不确定您将事件的 fd 值存储在哪里。您可以将它存储在事件中,但我有时会使用该事件来存储指向包含fd 数据以及其他信息的对象的指针......并且可能会发生错误,因为您没有将它存储在任何地方。我说不出来。
  • @myst 好的,我正在为我的问题制作一个更好、更完整的最小示例。
  • @Myst 很抱歉让您久等了,但我最终想通了,请参阅下面的答案。我创建了我的代码的最小版本,但它没有出现问题,我很困惑一段时间,但现在解决方案似乎很明显......

标签: linux sockets network-programming epoll


【解决方案1】:

对于我的问题,解决方案是像这样更改一行:

c.socket = accept(g.server_socket, NULL, NULL);

到这里:

c.socket = accept4(g.server_socket, NULL, NULL, SOCK_CLOEXEC);

如果其他人遇到此问题,请留意 dup()exec() 的来电。 dup() 可能导致 epoll 表现得好像它没有关闭,即使您已经关闭了添加到 epoll 的 fd。 epoll 只会在 fd 的所有副本关闭后才承认它已关闭。 exec() 本质上将与 dup() 对您拥有的每个不是使用 SOCK_CLOEXEC 标志创建的 fd 执行相同的操作...

【讨论】:

    猜你喜欢
    • 2013-09-07
    • 1970-01-01
    • 2016-01-09
    • 1970-01-01
    • 1970-01-01
    • 2020-09-16
    • 1970-01-01
    • 1970-01-01
    • 2015-09-12
    相关资源
    最近更新 更多