Пример типичной системно-прикладной разработки, реализация своей «очереди сообщений» (event loop).
Использованием #epoll, вместо «традиционных» select & poll, для асинхронной работы через polling’а (и мультиплексирования).
Отслеживание файловых дескрипторов через #epoll выглядит более современно, меньше копирования памяти между user space и kernel space. А при появлении ожидаемых данных можно напрямую переходить к объекту или структуре данных, что важно при наблюдении за несколькими файлами и\или соединениями. Устраняется поиск «сработавшего» файлового дескриптора в индексных массивах, полноценное О(1) во всех случаях. Можно сразу же работать с теми экземплярами объектов, которые оборачивают тот файл или udp-поток, tcp-, quic-соединение, где появились новые данные.
Есть несколько готовых к использованию «очередей сообщений» (event loop'ов) — #libev, #libuv, #libevent. Для некоторых агент-серверные решений и брокера #RabbitMQ это подходит. Однако, в некоторых случаях AMQP-библиотеки не скрещиваются с уже готовыми «очередями сообщений». Потому что агентская часть может активно использовать асинхронно-реактивное программирование с хорошей и проверенной «горизонтальной масштабируемостью». Т.е. на агентской части выполняется много работы и реализация сделана через sharing nothing многопоточность. Это такая парадигма, когда не просто достигается не только горизонтальная масштабируемость через lock-free\wait-free, а так же исключается много вредного, как тот же cache ping-pong или false sharing. Внутри агентов идёт своё управление потоками с выделениями памяти. Не только в плане «динамической памяти» (heap, аллокаторы а-ля #jemalloc от #Facebook), но и приколами вокруг pinning страниц, учёта #NUMA и даже huge pages(меньше промахов в #TLB).
Почему бы не использовать epoll?
Библиотека не обязана вычитывает данные целиком из потока (сокета), а может забирает данные лишь до тех пор, пока не насытится автомат состояний (finite-state machine). Например, выполняется парсинг сущностей AMQP-протокола, которые, по мере накопления, передаются в обработчики указанные клиентом библиотеки.
И это плохо соотносится с тем, что используя #epoll надо выбирать какой вариант оповещений использовать:
• «по уровню» (level-triggered),
• «по фронту» (edge-triggered).
Особенности поведения отдельно взятой библиотеки может не позволять использовать работу «по фронту» (edge-triggered), т.к. библиотека не вычитывает полностью все данные из файловых дескрипторов.
Можно быть хоть пять раз technical lead и всё это прекрасно знать, но следует помнить, что как только в коде появляется флаг EPOLLET, то необходимо проводить аудит работы с потоками данных. Это избавляет команду от многих заморочек вокруг тестирования и ковыряния с каким-то совершенно непонятным поведением кода.
Про «Edge Triggered Vs Level Triggered interrupts»
#programming #linux #softdev #трудовыебудни