边缘触发的 epoll 和 oneshot 只报告一次

2023-12-10

我目前正在添加从创建的 sockfdsaccept到具有以下事件的 epoll 实例:

const int EVENTS = (
    EPOLLET |
    EPOLLIN |
    EPOLLRDHUP |
    EPOLLONESHOT |
    EPOLLERR |
    EPOLLHUP);

一旦触发事件,我将其传递给处理程序线程,读取并重新启用 sockfdepoll_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);
}

当我做的时候EPOLL_CTL_MOD操作时,我没有收到任何错误,但从未收到其他事件的通知。如果我在第一个事件之后重复读取循环,它将读取客户端发送的所有后续数据,因此我知道数据正在传入并且 fd 仍然打开并工作。

从检查strace,线程是从创建的clone并有旗帜CLONE_FILES,所以所有线程共享同一个fd表。

从单独的线程重新启用 fd 来读取事件的正确方法是什么?


但是,我只收到一次 EPOLLIN 事件。另外,如果我在收到第一个事件后随时终止客户端,我也不会收到挂断事件。

epoll_ctl(2) 的手册页说:

EPOLLONESHOT(自 Linux 2.6.2 起) 设置关联文件描述符的一次性行为。 这意味着在事件被拉出后 epoll_wait(2) 内部关联的文件描述符 禁用并且 epoll 不会报告其他事件 界面。用户必须使用 EPOLL_CTL_MOD 调用 epoll_ctl() 使用新的事件掩码重新装备文件描述符。

在您的情况下,当您收到第一个事件时,epoll 会禁用您的 sockfd。 当您使用重新启用 sockfd 时EPOLL_CTL_MOD,它会通知内核收到的所有事件after重新注册。因此,第一次通知和重新注册之间的任何事件都将丢失。这可能是未获取任何挂断事件或数据的原因。

去除EPOLLONESHOTfrom events 将更正您的代码,最终您也不需要重新启用 sockfd 。

既然你正在使用EPOLLET,也不会有任何性能问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

边缘触发的 epoll 和 oneshot 只报告一次 的相关文章

随机推荐