Как работает epoll/kqueue и почему они быстрее select/poll
Когда сервер обрабатывает множество соединений одновременно, важно эффективно следить за готовностью сокетов к чтению и записи. В Linux и BSD для этого есть несколько механизмов: select, poll, epoll (Linux) и kqueue (BSD, macOS). Каждый из них решает одну задачу — уведомлять программу о событиях на файловых дескрипторах — но работает по-разному.
select и poll
select() и poll() — старые API, которые существуют десятки лет. Они работают так:
- Перед каждым вызовом функции программа формирует массив дескрипторов сокетов, за которыми хочет следить.
- Функция возвращает, какие дескрипторы готовы к чтению/записи.
Проблемы select/poll
- Линейная проверка
selectиpollпросматривают весь список дескрипторов каждый раз- Время выполнения растёт пропорционально количеству сокетов
- Ограничение на количество дескрипторов
selectимеет лимит FD_SETSIZE (обычно 1024)- Poll ограничений формально нет, но тоже растёт нагрузка при большом числе соединений
- Каждый вызов копирует массивы
- Программе нужно передавать массив всех FD в ядро и обратно
- Это создаёт лишние системные вызовы и копирование данных
Эти ограничения делают select/poll непригодными для серверов с тысячами одновременных соединений.
epoll (Linux)
epoll был создан для Linux как решение проблем select/poll.
Особенности:
- Работа с «событийным списком»
- Программа регистрирует интересующие события один раз через
epoll_ctl - После этого ядро само отслеживает, какие дескрипторы готовы
- Программа регистрирует интересующие события один раз через
- События возвращаются как готовые
- Вызов
epoll_waitвозвращает только активные дескрипторы
- Вызов
- Производительность O(1)
- Независимо от числа FD, время обработки событий не растёт линейно
Режимы работы epoll
- Level-triggered (LT)
- Повторно уведомляет, пока дескриптор готов
- Поведение похоже на poll, но эффективнее
- Edge-triggered (ET)
- Уведомление один раз при изменении состояния
- Требует неблокирующих сокетов и внимательной обработки
- Позволяет минимизировать лишние вызовы
Преимущества epoll
- Отлично подходит для тысяч соединений
- Минимальные копирования данных между ядром и пользователем
- Гибкость: LT и ET режимы
kqueue (BSD, macOS)
kqueue — аналог epoll в системах BSD и macOS.
Особенности:
- Event queue
- Программа регистрирует события, ядро следит за ними
- Поддержка множества типов событий
- Не только сокеты, но и файлы, сигналы, таймеры
- Производительность O(1)
- Очень эффективно при большом числе дескрипторов
Отличия от epoll
- epoll специализируется на файловых дескрипторах (сокеты, файлы)
- kqueue универсальнее, покрывает больше типов событий
- API отличается, но принципы похожи: регистрация событий, ожидание активных
Практическая разница
| Параметр | select/poll | epoll/kqueue |
|---|---|---|
| Производительность | O(N) | O(1) |
| Копирование FD | да | нет (только при регистрации) |
| Масштабируемость | до ~1000 FD | тысячи и десятки тысяч FD |
| Поддержка событий | только готовность к read/write | read/write, таймеры, сигналы (kqueue) |
Почему это важно
Современные серверы (Nginx, HAProxy, Node.js, Redis) обрабатывают десятки тысяч соединений. Использование select/poll на такой нагрузке приведёт к:
- высокому CPU на перебор дескрипторов
- задержкам при пиковых нагрузках
Использование epoll или kqueue позволяет:
- обрабатывать множество соединений эффективно
- минимизировать количество системных вызовов
- уменьшить latency и повысить пропускную способность
Пример на Linux (epoll)
int epfd = epoll_create1(0);
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event);
while(1) {
int n = epoll_wait(epfd, events, MAX_EVENTS, -1);
for(int i=0; i<n; i++) {
if(events[i].events & EPOLLIN) handle_read(events[i].data.fd);
}
}Принцип: один раз зарегистрировали интересующие события, и ядро само сообщает, когда что-то готово.
Итог
- select/poll — простые, старые, подходят для небольшого числа соединений
- epoll/kqueue — современные, эффективные, масштабируемые, используются в высоконагруженных серверах
Понимание этих механизмов важно при разработке сетевых приложений, чтобы выбрать правильный инструмент и обеспечить максимальную производительность и низкую задержку.
Настроить мониторинг за 30 секунд
Надежные оповещения о даунтаймах. Без ложных срабатываний