В справочной странице 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-страницу должным образом, или что-то здесь работает.