0Pricing
C Academy · Урок

Настройка epoll

Создайте и зарегистрируйте набор epoll.

«Настройка epoll» — бесплатный урок C Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C Academy содержит 4 уроков всего.

Создание экземпляра epoll

Всё начинается с экземпляра epoll, который сам является файловым дескриптором. Создайте его с помощью epoll_create1().

Аргумент представляет собой поле флагов: передайте 0 для поведения по умолчанию или EPOLL_CLOEXEC, чтобы дескриптор автоматически закрывался при выполнении exec(). Возвращённый дескриптор передаётся во все остальные вызовы epoll.

int epfd = epoll_create1(EPOLL_CLOEXEC);
if (epfd == -1) { perror("epoll_create1"); exit(1); }

epoll_create1 и epoll_create

Старая функция epoll_create(int size) принимала подсказку о размере, но теперь ядро её игнорирует; требовалось только, чтобы значение было положительным.

Предпочитайте epoll_create1(): вместо бессмысленного размера она принимает флаги и позволяет атомарно запросить EPOLL_CLOEXEC, избежав отдельного вызова fcntl().

/* legacy form, size arg ignored by kernel */
int epfd = epoll_create(1);

Структура epoll_event

И интересующие события, и результаты представляются с помощью struct epoll_event. В ней есть битовая маска events и объединение data.

Объединение data содержит любые данные, которые нужно вернуть при срабатывании события: обычно это data.fd, но это также может быть указатель data.ptr на созданный Вами объект соединения.

struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = listen_fd;

Регистрация с помощью EPOLL_CTL_ADD

epoll_ctl() управляет списком интересующих событий. Используйте операцию EPOLL_CTL_ADD, чтобы начать отслеживать дескриптор.

Передайте дескриптор epoll, операцию, целевой дескриптор и указатель на Ваш epoll_event. После этого epoll будет сообщать о готовности этого дескриптора.

struct epoll_event ev = { .events = EPOLLIN, .data.fd = listen_fd };
if (epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ev) == -1)
    perror("epoll_ctl: add");

Флаги событий: EPOLLIN и EPOLLOUT

Маска events указывает, какие события Вас интересуют. EPOLLIN означает, что дескриптор доступен для чтения, а EPOLLOUT — что он доступен для записи.

Обычно EPOLLIN отслеживают у сокета, принимающего соединения (чтобы принимать их), и у клиентских сокетов (чтобы читать данные). Добавляйте EPOLLOUT только при наличии буферизованных данных, которые не удалось отправить полностью.

ev.events = EPOLLIN | EPOLLOUT;

Флаги ошибок и разрыва соединения

Ядро всегда сообщает об определённых состояниях, даже если Вы их не запрашивали: EPOLLERR — об ошибках, EPOLLHUP — о разрыве соединения, а EPOLLRDHUP — о частичном закрытии соединения узлом-партнёром.

Проверяйте эти флаги в обработчике: игнорирование EPOLLHUP у неработающего сокета приводит к бесконечному вращению цикла обработки событий.

if (ev.events & (EPOLLERR | EPOLLHUP)) {
    close(ev.data.fd);
    continue;
}

Изменение интересующих событий с помощью EPOLL_CTL_MOD

После регистрации дескриптора можно изменить его маску событий, не удаляя его. Используйте EPOLL_CTL_MOD.

Распространённый шаблон таков: обычно соединение отслеживает только EPOLLIN, затем переключается на EPOLLIN | EPOLLOUT, когда его буфер отправки заполняется, и возвращается обратно после очистки буфера.

ev.events = EPOLLIN | EPOLLOUT;
ev.data.fd = conn_fd;
epoll_ctl(epfd, EPOLL_CTL_MOD, conn_fd, &ev);

Удаление с помощью EPOLL_CTL_DEL

Когда соединение закрывается, прекратите его отслеживание с помощью EPOLL_CTL_DEL. Последний аргумент epoll_event игнорируется и в современных ядрах может иметь значение NULL.

Учтите, что закрытие дескриптора автоматически удаляет его из всех списков интересующих событий epoll, однако явное удаление перед закрытием делает учёт ресурсов понятнее.

epoll_ctl(epfd, EPOLL_CTL_DEL, conn_fd, NULL);
close(conn_fd);

Уровневая и триггерная обработка

По умолчанию epoll работает в уровневом режиме: пока дескриптор доступен для чтения, каждый вызов epoll_wait() сообщает о нём. Такой режим проще и снисходительнее к ошибкам.

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

ev.events = EPOLLIN | EPOLLET;  /* edge-triggered */
epoll_ctl(epfd, EPOLL_CTL_ADD, conn_fd, &ev);

Сохранение указателя на контекст

Вместо data.fd можно сохранить в data.ptr указатель на структуру для отдельного соединения. При срабатывании события Вы сразу получите этот указатель, и таблица поиска не потребуется.

Помните об объединении: если Вы задали data.ptr, нельзя одновременно читать data.fd из того же события.

struct conn *c = malloc(sizeof *c);
c->fd = conn_fd;
ev.events = EPOLLIN;
ev.data.ptr = c;
epoll_ctl(epfd, EPOLL_CTL_ADD, conn_fd, &ev);

Освобождение ресурсов

Экземпляр epoll — это всего лишь файловый дескриптор, поэтому при завершении работы сервера освободите его с помощью close().

Закрытие дескриптора epoll освобождает список интересующих событий в ядре. Все отслеживаемые сокеты, которые ещё открыты, остаются открытыми; закройте их отдельно, чтобы освободить связанные с ними ресурсы.

close(epfd);

Быстрая проверка

Проверьте, насколько хорошо Вы усвоили API настройки epoll.

Итоги

Вы создаёте экземпляр с помощью epoll_create1(), а затем управляете интересующими событиями через epoll_ctl(), используя ADD, MOD и DEL. Каждый struct epoll_event содержит маску events и объединение data.

Выбирайте уровневой режим для простоты или EPOLLET для производительности триггерного режима. Далее мы задействуем эти компоненты в цикле обработки событий.

Часто задаваемые вопросы

Урок «Настройка epoll» бесплатный?

Да — полный текст урока «Настройка epoll» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C Academy, подпишись на CoddyKit PRO. Курс C Academy содержит 4 уроков всего.

Чему я научусь в уроке «Настройка epoll»?

Создайте и зарегистрируйте набор epoll. Ты практикуешь C Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C Academy?

Предыдущий опыт не требуется. C Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Настройка epoll»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C Academy?

Да. Каждый урок C Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Блокирующий и неблокирующий ввод-вывод
  2. Настройка epoll
  3. Цикл обработки событий
  4. Простой эхо-сервер
← Назад к C Academy