是Epoll与边沿触发和单稳只有一次报告 [英] Epoll with edge triggered and oneshot only reports once

查看:140
本文介绍了是Epoll与边沿触发和单稳只有一次报告的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在增加从创建sockfds接受以一个epoll的实例以下事件:

  const int的活动=(
    EPOLLET |
    EPOLLIN |
    EPOLLRDHUP |
    EPOLLONESHOT |
    EPOLLERR |
    EPOLLHUP);

一旦事件被触发,我将其传递给一个处理线程,读,然后重新启用通过 epoll_ctl 具有相同的标志的sockfd。不过,我只收到 EPOLLIN 事件一次。另外,如果我杀了接收第一个事件后,可随时在客户端,我不明白挂断事件无论是。从阅读手册页,我想我明白与EdgeTriggered和ONESHOT正确的做法。

下面是一些伪code为我使用的过程:

  const int的活动=(
    EPOLLET |
    EPOLLIN |
    EPOLLRDHUP |
    EPOLLONESHOT |
    EPOLLERR |
    EPOLLHUP);无效event_loop()
{
    结构epoll_event事件;
    结构epoll_event *事件;
    事件=释放calloc(100,sizeof的事件);
    而(1)
    {
        INT X;
        INT NUM_EVENTS = epoll_wait(epfd的,事件,100,-1);
        为(X = 0; X&下; NUM_EVENTS; X ++)
        {
            another_thread(FD);
        }
    }
}无效another_thread(INT FD)
{
    //读取的东西,直到EAGAIN    结构epoll_event事件;
    event.data.fd = FD;
    event.events =活动;
    epoll_ctl(epfd的,EPOLL_CTL_MOD,FD&安培;事件);
}

当我做 EPOLL_CTL_MOD 的操作,我没有收到任何错误,但从来没有得到通知的其他事件。如果我离开重复读取循环的第一个事件之后,它会读取客户端发送的所有后续数据,所以我知道数据是进来和FD仍处于打开状态和工作。

从检查 strace的,从的克隆并有标志 CLONE_FILES ,因此所有线程共享相同的fd表。

什么是正确的方式,从一个单独的线程重新启用读事件FD?


解决方案

  

不过,我只收到EPOLLIN事件一次。另外,如果我杀了接收第一个事件后,可随时在客户端,我不明白挂断事件无论是。


有关epoll_ctl手册页(2)说:


  

    

EPOLLONESHOT(因为Linux 2.6.2)
                  设置相关联的文件描述符单触发行为。
                  这意味着,一个事件后与拉出
                  epoll_wait(2)关联的文件描述符是内部
                  残疾并没有其他事件将被epoll的报告
                  接口。用户必须调用epoll_ctl()与EPOLL_CTL_MOD
                  再装一个新的事件掩码的文件描​​述符。


  

在你的情况,当你得到第一个事件,epoll的禁用你的sockfd。
使用 EPOLL_CTL_MOD ,将通知由内核收到的所有事件当您重新启用的sockfd 之后的重新注册。因此,首先通知和重新注册之间的任何事件都将丢失。这可能是原因没有得到任何挂起的事件或数据。

删除 EPOLLONESHOT 从事件会纠正你的code,最终你不也需要重新启用的sockfd。

此外,由于使用的是 EPOLLET ,将不会有任何性能问题也。

I'm currently adding sockfds created from accept to an epoll instance with the following events:

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

Once an event is triggered, I pass it off to a handler thread, read and then re-enable the sockfd through epoll_ctl with the same flags. However, I only receive the EPOLLIN event one time. Also, if I kill the client anytime after the first event is received, I do not get hangup events either. From reading the man pages, I thought I understood the correct approach with EdgeTriggered and OneShot.

Below is some pseudo code for the process I'm using:

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);
}

When I do the EPOLL_CTL_MOD operation, I do not receive any errors, but never get notified of other events. If I leave the read loop on repeat after the first event, it will read all subsequent data sent by client, so I know that the data is coming in and the fd is still open and working.

From checking strace, threads are created from clone and have the flag CLONE_FILES, so all threads share the same fd table.

What is the correct way to re-enable a fd for read events from a separate thread?

解决方案

However, I only receive the EPOLLIN event one time. Also, if I kill the client anytime after the first event is received, I do not get hangup events either.

man page for epoll_ctl(2) says that:

EPOLLONESHOT (since Linux 2.6.2) Sets the one-shot behavior for the associated file descriptor. This means that after an event is pulled out with epoll_wait(2) the associated file descriptor is internally disabled and no other events will be reported by the epoll interface. The user must call epoll_ctl() with EPOLL_CTL_MOD to rearm the file descriptor with a new event mask.

In your case, when you get the first event, epoll disables your sockfd. When you re-enable your sockfd using EPOLL_CTL_MOD, it will notify all the events that are received by kernel after the re-registration. So any event between first notification and re-registration will be lost. This can be reason for not getting any hangup events or data.

Removing EPOLLONESHOT from events will correct your code, eventually you don't need to re-enable sockfd also.

And since you are using EPOLLET, there won't be any performance issue also.

这篇关于是Epoll与边沿触发和单稳只有一次报告的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆