Зачем использовать неблокирующий fd в функции epoll, запускаемой по краю?

Я прочитал документ о функции epoll, запускаемой по краю, в Интернете следующим образом:

1. The file descriptor that represents the read side of a pipe (rfd) is registered on the epoll instance.
2. A pipe writer writes 2 kB of data on the write side of the pipe.
3. A call to epoll_wait(2) is done that will return rfd as a ready file descriptor.
4. The pipe reader reads 1 kB of data from rfd.
5. A call to epoll_wait(2) is done.
.......
.......

Предлагаемый способ использования epoll в качестве интерфейса, инициируемого фронтом (EPOLLET), заключается в следующем: i) Используйте неблокирующие файловые дескрипторы ii) Вызовите epoll_wait для события только после того, как read(2) или write(2) вернет EAGAIN.

Я понял 2, но я не мог понять, почему используются неблокирующие файловые дескрипторы.

Может ли кто-нибудь объяснить, почему используются неблокирующие файловые дескрипторы? Почему можно использовать блокирующие файловые дескрипторы в функции epoll, инициируемой уровнем?


person user1804694    schedule 01.02.2013    source источник


Ответы (3)


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

Если fd был открыт с блокировкой, то этот последний read() или write() также будет заблокирован, и у вас не будет возможности вернуться к вызову epoll() для ожидания всего набора fd. При открытии без блокировки возвращаются последние read()/write(), и у вас есть возможность вернуться к опросу.

Это не так важно при использовании epoll() в режиме триггера уровня, так как в этом случае epoll() вернется немедленно, если есть какие-либо данные, которые нужно получить. Итак, цикл (псевдокод), такой как:

while (1) {
  epoll();
  do_read_write();
}

будет работать, так как вы гарантированно вызовете do_read_write(), пока есть данные. При использовании epoll, инициируемого краем, существует вероятность того, что уведомление о доступности новых данных будет пропущено, если оно появится между окончанием do_read_write() и следующим вызовом epoll().

person sheu    schedule 01.02.2013
comment
Как быть с возможностью пропуска уведомления о наличии новых данных? - person cong; 20.06.2017
comment
Последняя часть этого ответа, похоже, не соответствует действительности. События, которые произошли, пока вы не ожидаете, также будут зарегистрированы и возвращены при следующем запуске epoll_wait. Но настоящая проблема в том, что если вы не прочитаете все данные из сокета с помощью этой функции do_read_write, и вы снова попадете в epoll_wait. Затем с поведением, запускаемым по краю, вы застрянете там на неопределенный срок, потому что тогда события создаются только в том случае, если есть изменение в читаемости (или записи). Проблема не в том, когда приходят данные, а в том, что вы не прочитали их все. - person clime; 26.09.2019

Я предполагаю, что это из-за семантики краевого срабатывания. Триггер по фронту, согласно семантике, вызовет другое событие только после получения EAGAIN. В случае блокировки сокетов EAGAIN отсутствует. Вы могли бы определить это каким-то другим способом, но это то, как это определяет Linux. Другими словами, если вы используете блокирующие сокеты, вы понятия не имеете, когда можно безопасно вызывать epoll_wait.

person Sharvanath    schedule 13.09.2013

Вы должны прочитать все или записать все данные в режиме ET epoll, потому что режим et сработал один раз после изменения флага. Когда вы прочитали все данные, поток должен зависнуть, если вы используете чтение или запись блока. Поэтому необходимо использовать неблокировку.

person FENH    schedule 17.04.2017