Epoll в режиме EPOLLET возвращает 2 EPOLLIN перед чтением из сокета

В справочной странице epoll сказано, что fd, зарегистрированный с помощью EPOLLET (срабатывает по фронту), не должен дважды уведомлять EPOLLIN, если чтение не было выполнено.
Поэтому после EPOLLIN вам нужно очистить буфер, прежде чем epoll_wait сможет вернуть новый EPOLLIN на новые данные.

Однако у меня возникают проблемы с этим подходом, поскольку я вижу повторяющиеся события EPOLLIN для нетронутых fds.
Это вывод strace, 0x200 — это EPOLRDHUP, который еще не определен в моих заголовках glibc, но определен в ядре.

30285 epoll_ctl(3, EPOLL_CTL_ADD, 9, {EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET|0x2000, {u32=9, u64=9}}) = 0
30285 epoll_wait(3, {{EPOLLIN, {u32=9, u64=9}}}, 10, -1) = 1
30285 epoll_wait(3, {{EPOLLIN, {u32=9, u64=9}}}, 10, -1) = 1
30285 epoll_wait(3,  <unfinished ...>
30349 epoll_ctl(3, EPOLL_CTL_DEL, 9, NULL) = 0
30306 recv(9, "7u\0\0\10\345\241\312\t\20\f\32\r\10\27\20\2\30\200\10 \31(C0\17\32\r\10\27\20\2\30"..., 20000, 0) = 20000
30349 epoll_ctl(3, EPOLL_CTL_DEL, 9, NULL) = -1 ENOENT (No such file or directory)
30305 recv(9, " \31(C0\17\32\r\10\27\20\2\30\200\10 \31(C0\17\32\r\10\27\20\2\30\200\10 \31("..., 20000, 0) = 10011

Таким образом, после добавления fd номер 9 я получаю 2 последовательных события EPOLLIN до получения дескриптора файла, трассировка системного вызова показывает, как я удаляю fd перед чтением, но это должно произойти только один раз, по одному на событие.
Так что либо я я не читаю man-страницу должным образом, или что-то здесь работает.


person Arkaitz Jimenez    schedule 20.04.2010    source источник


Ответы (3)


Я думаю, вы пропустили эту часть страницы руководства epoll:

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

То есть: вы получили два фрагмента данных, поступающих в вашу очередь приема до того, как произошло ваше первое read(), что означает, что вы получили два события epoll. Похоже, что вам нужен EPOLLONESHOT, который атомарно удалит дескриптор файла из набора опросов, когда на нем произойдет событие (поэтому вам не нужно будет делать EPOLL_CTL_DEL).

person caf    schedule 20.04.2010
comment
Хороший ответ. У меня другой вопрос: нужен ли вообще EPOLLET, если установлен EPOLLONESHOT? в большинстве случаев, когда мы получаем событие EPOLLONESHOT, мы будем выполнять ввод-вывод до тех пор, пока fd не заблокирует нашу операцию ввода-вывода, а затем мы переустановим событие. - person zach; 25.06.2015

Триггер Edge просто означает (если вы не использовали EPOLLONESHOT), что вы получите 1 событие, когда что-то входит в буфер (ядра).

Таким образом, если вы получите 1 событие EPOLLIN и ничего с этим не сделаете, вы получите еще одно событие EPOLLIN, когда в следующий раз поступят какие-то данные для этого дескриптора. прочитать любые данные, указанные первым событием.

person nos    schedule 20.04.2010

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

Обычно вы будете уведомлены о событии для тех же данных, если вы их не читаете. Однако с EPOLLONESHOT не чтение данных совершенно законно, и они будут просто проигнорированы. Следовательно, никакие дальнейшие события не будут генерироваться.

person Josh Paul    schedule 20.07.2011