Наблюдаются ли события epoll, когда не epoll_waiting

Я довольно новичок в программировании на основе событий. Я экспериментирую с epoll Режим края, который, по-видимому, только сигнализирует о файлах, которые стали готовыми для чтения / записи (в отличие от режима уровня, который сигнализирует обо всех готовых файлах, независимо от того, были ли они уже готовы или только что стали готовыми).

Что мне не понятно, так это то, что в пограничном режиме мне сообщают о событиях готовности, которые происходят, пока я не epoll_wait Инг? А как насчет событий в одноразовых файлах, которые еще не были изменены?

Чтобы проиллюстрировать, почему я спрашиваю об этом, рассмотрим следующий сценарий:

  • подключено 10 неблокирующих розеток
  • конфигурировать epoll_ctl реагировать, когда сокеты готовы для чтения, в режиме edge + oneshot: EPOLLET | EPOLLONESHOT | EPOLLIN
  • epoll_wait что-то случится (сообщает максимум 10 событий)
  • Linux запускает мой процесс и сообщает, что сокеты №1 и №2 готовы
  • я read и обработать сокет данных № 1 (до E_AGAIN)
  • я read и обработать сокет данных № 2 (до E_AGAIN)
  • Пока я это делаю, сокет S получает данные
  • Я обработал все события, поэтому перезагружаю запущенные файлы epoll_ctl в EPOLL_CTL_MOD режим из-за прицела
  • мой цикл возвращается к epoll_wait следующая серия событий

Хорошо, так будет последний epoll_wait всегда получать уведомления о готовности сокета S? Событие, если S #1 (то есть не перевооружено)?

1 ответ

Решение

Я экспериментирую с краевым режимом epoll, который, по-видимому, только сигнализирует о файлах, которые стали готовыми для чтения / записи (в отличие от уровня, который сигнализирует о всех готовых файлах, независимо от того, были ли они уже готовы или просто стали готовыми)

Во-первых, давайте получим четкое представление о системе, вам нужна точная мысленная модель работы системы. Ваш взгляд на epoll(7) не совсем точно.

Разница между триггерным и триггерным уровнем - это определение того, что именно делает событие. Первый генерирует одно событие для каждого действия, которое было подписано в дескрипторе файла; как только вы используете событие, оно исчезает, даже если вы не использовали все данные, которые сгенерировали такое событие. OTOH, последний продолжает генерировать одно и то же событие снова и снова, пока вы не используете все данные, которые сгенерировали событие.

Вот пример, который приводит в действие эти концепции, явно похищенные из man 7 epoll:

  1. Файловый дескриптор, представляющий сторону чтения канала (rfd), зарегистрирован в экземпляре epoll.

  2. Канал записи пишет 2 КБ данных на стороне записи канала.

  3. Выполнен вызов epoll_wait(2), который вернет rfd в качестве готового дескриптора файла.

  4. Канальный считыватель считывает 1 кБ данных из RFD.

  5. Вызов epoll_wait(2) завершен.

Если дескриптор файла rfd был добавлен в интерфейс epoll с использованием флага EPOLLET (edge-triggered), вызов epoll_wait(2), выполненный на шаге 5, вероятно, будет зависать, несмотря на доступные данные, все еще присутствующие в буфере ввода файла; тем временем удаленный узел может ожидать ответа на основе данных, которые он уже отправил. Причина этого заключается в том, что режим запуска по фронту доставляет события только тогда, когда происходят изменения в дескрипторе отслеживаемого файла. Таким образом, на шаге 5 вызывающая сторона может в конечном итоге ожидать некоторые данные, которые уже присутствуют во входном буфере. В вышеприведенном примере событие на rfd будет сгенерировано из-за того, что запись выполнена в 2, а событие используется в 3. Поскольку операция чтения, выполненная в 4, не использует все данные буфера, вызов epoll_wait(2) выполнен на шаге 5 может заблокироваться на неопределенный срок.

Короче говоря, принципиальное различие заключается в определении "события": инициируемые фронтом обрабатывают события как единое целое, которое вы потребляете один раз; триггерный уровень определяет потребление события как эквивалентное потреблению всех данных, принадлежащих этому событию.

Теперь, со всем этим, давайте ответим на ваши конкретные вопросы.

в пограничном режиме мне сообщают о событиях готовности, которые происходят, пока я не epoll_waiting

Да, вы. Внутренне ядро ​​помещает в очередь интересные события, которые произошли с каждым файловым дескриптором. Они возвращаются при следующем вызове epoll_wait(2), так что вы можете быть уверены, что вы не потеряете события. Ну, может быть, не совсем при следующем вызове, если ожидают другие события и буфер событий передается epoll_wait(2) не могу вместить их всех, но суть в том, что в конечном итоге об этих событиях будет сообщено.

А как насчет событий в одноразовых файлах, которые еще не были изменены?

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

Хорошо, так будет ли последний epoll_wait всегда уведомляться о готовности сокета S? Событие, если S #1 (то есть не перевооружено)?

Учитывая то, что я сказал выше, к настоящему времени должно быть довольно ясно: да, это будет. Вы не потеряете ни одного события. epoll предлагает сильные гарантии, это круто. Он также поточно-ориентированный, и вы можете ждать одного и того же epoll fd в разных потоках и одновременно обновлять подписку на события. epoll очень мощный, и стоит потратить время на его изучение!

Другие вопросы по тегам