Эполл с активированным краем и один выстрел сообщает только один раз

В настоящее время я добавляю sockfds, созданные из accept, в экземпляр epoll со следующими событиями:

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

Как только событие инициируется, я передаю его потоку обработчика, читаю и затем снова включаю sockfd через epoll_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_FILESТаким образом, все потоки используют одну и ту же таблицу fd.

Как правильно включить FD для чтения событий из отдельного потока?

1 ответ

Решение

Тем не менее, я получаю событие EPOLLIN только один раз. Кроме того, если я убиваю клиента в любое время после получения первого события, я также не получаю события зависания.

Страница man для epoll_ctl(2) говорит, что:

EPOLLONESHOT (начиная с Linux 2.6.2) Устанавливает однократное поведение для соответствующего дескриптора файла. Это означает, что после извлечения события с помощью epoll_wait(2) соответствующий дескриптор файла будет внутренне отключен, и интерфейс epoll не сообщит о других событиях. Пользователь должен вызвать epoll_ctl() с EPOLL_CTL_MOD, чтобы перевооружить файловый дескриптор новой маской события.

В вашем случае, когда вы получаете первое событие, epoll отключает ваш sockfd. При повторном включении вашего sockfd с помощью EPOLL_CTL_MOD, он будет уведомлять все события, полученные ядром после перерегистрации. Таким образом, любое событие между первым уведомлением и перерегистрацией будет потеряно. Это может быть причиной отсутствия каких-либо событий зависания или данных.

Удаление EPOLLONESHOT из событий исправит ваш код, в конце концов вам также не нужно повторно включать sockfd.

И так как вы используете EPOLLETтакже не будет проблем с производительностью.

Другие вопросы по тегам