Epoll в режиме EPOLLET, возвращающий 2 EPOLLIN перед чтением из сокета
На странице руководства epoll говорится, что fd, зарегистрированный в EPOLLET(инициируемый фронтом), не должен дважды уведомлять EPOLLIN, если не было выполнено чтение.
Поэтому после EPOLLIN вам нужно очистить буфер до того, как epoll_wait сможет вернуть новый EPOLLIN для новых данных.
Однако у меня возникают проблемы с этим подходом, так как я вижу дублированные события EPOLLIN для нетронутых fds.
Это вывод strace, 0x200 - это EPOLLRDHUP, который еще не определен в моих заголовках 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-страницу правильно, либо что-то здесь работает.
3 ответа
Я думаю, что вы пропустили эту часть epoll
справочная страница:
Так как даже с запуском по краю epoll может быть сгенерировано несколько событий при получении нескольких порций данных, вызывающая сторона имеет возможность указать
EPOLLONESHOT
флаг, чтобы epoll отключил связанный файловый дескриптор после получения события сepoll_wait(2)
, КогдаEPOLLONESHOT
флаг указан, вызывающий должен переписать дескриптор файла, используяepoll_ctl(2)
сEPOLL_CTL_MOD
,
То есть: у вас есть две порции данных, поступающих в очередь на получение до вашего первого read()
произошло, что означает, что вы получили два события epoll. Это похоже на EPOLLONESHOT
это то, что вам нужно, что атомарно удалит дескриптор файла из набора опроса, когда с ним произойдет событие (так что вам не нужно делать EPOLL_CTL_DEL
).
Краевой триггер просто означает (если вы не использовали EPOLLONESHOT), что вы получите 1 событие, когда что-то попадет в (ядро) буфер.
Таким образом, если вы получаете 1 событие EPOLLIN и ничего не делаете с ним, вы получите еще один EPOLLIN в следующий раз, когда в этот дескриптор поступят какие-то данные - если новые данные не поступят, вы, тем не менее, не получите событие, даже если вы этого не сделали читать любые данные, как указано первым событием.
Если говорить кратко, EPOLLONESHOT просто означает, что если вы не прочитаете данные, которые должны прочитать, они будут отброшены.
Как правило, вы будете уведомлены с событием для тех же данных, если вы не читаете их. С EPOLLONESHOT, однако, не чтение данных совершенно законно, и они будут просто проигнорированы. Следовательно, дальнейшие события не будут генерироваться.