Введение по linux epoll

Введение

Данная заметка родилась в попытках разобраться с базовым принципом работы linux epoll, так как он часто используется в разных языках программирования для реализации асинхронности.

Все что написано ниже, сугубо мое понимание и оно не претендует на 100% точность, если кто-то оставит полезные примечания, то буду благодарен.

Файловые дескрипторы ОС

Все процессы ссылаются на потоки ввода-вывода (IO) через дескрипторы. Каждый процесс поддерживает таблицу файловых дескрипторов, которые ему доступны.

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

Дескрипторы либо создаются явно, либо наследуются от родительского процесса.

Как работает epoll.

Epoll структура в ядре linux, которая позволяет мультиплексировать IO операции для нескольких файловых деcкрипторов.

Создание экземпляра epoll происходит системным вызовом epoll_create, результатом возвращается файловый дескриптор, который вызывающий процесс может использовать для добавления, изменения и удаления других файловых дескрипторов для которых будет отслеживать IO операции созданный экземпляр epoll.

Далее процесс с помощью системного вызова epoll_ctl, добавляет файловые дескрипторы и события (структура epoll_event) для отслеживания их созданным экземпляром epoll.

Все добавленные дескрипторы помещаются набор epoll (epoll set) и в последствии, когда любой из зарегистрированных дескрипторов становится готовым для IO операций, они считаются находящимися в списке готовности (ready list). Список готовности является подмножеством набора epoll.

Когда наступает какое-то из зарегистрированных событие поток уведомляется об этом системным вызовом epoll_wait, который . В зависимости от заданного таймаута epoll_wait имеет следующее поведение:

  • когда для тайм-аута задан 0, epoll_wait не блокируется, а возвращается сразу после проверки того, какие файловые дескрипторы в набор epoll для текущего процесса готовы (дескрипторы из списка готовности)
  • когда для тайм-аута задан -1, epoll_wait будет блокироваться «навсегда». Когда epoll_wait блокируется, ядро может перевести процесс в спящий режим до тех пор, пока не вернется epoll_wait. Блокироваться epoll_wait будет до тех пор, пока не наступит одно из условий:
    • один или несколько дескрипторов, указанных в наборе epoll для текущего процесса готовы (дескрипторы из списка готовности)
    • вызов прерван обработчиком сигнала
  • когда для тайм-аута задано неотрицательное и ненулевое значение, epoll_wait будет блокироваться до тех пор, не наступит одно из условий:
    • один или несколько дескрипторов, указанных в наборе epoll для текущего процесса готовы
    • вызов прерван обработчиком сигнала
    • истекло общее время указанное в тайм-ауте (миллисекунды)

Есть несколько способов нотификации о событии (задаются влагами в epoll_event при регистрации дескриптора):

  • level-triggered (флаг EPOLLIN) - epoll_wait разблокируется если дескриптор находится в заданном состоянии и будет полагать его активным пока данное состояние не будет снято
  • edge-triggered (флаги EPOLLIN | EPOLLET) - epoll_wait разблокируется только по изменению текущего данного заказанного состояния.

Подробнее этот вопрос рассмотрен в [2].

Вывод

После изучения материала у меня сложилось впечатление, что производительность решений на базе epoll очень сильно зависит от конкретной реализации механизма работы с ним. И чтобы это узнать необходимо лезть в кишки конкретного инструмента.

Ссылки

  1. The method to epoll’s madness
  2. Вся правда о linux epoll
 
comments powered by Disqus