Эполл с активированным краем и один выстрел сообщает только один раз
В настоящее время я добавляю 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
также не будет проблем с производительностью.