для предоставления набора файловых дескрипторов, a
select
с той же целью — три разных структуры
fd_set
,
epoll
перемещает эти наборы файловых дескрипторов в ядро, а не хранит их в адресном пространстве программы. На каждый из этих наборов ссылаются с помощью дескриптора
epoll
, являющегося файловым дескриптором, который можно применять только для системных вызовов
epoll
. Новые дескрипторы epoll распределяются системным вызовом
epoll_create
.
#include <sys/epoll.h>
int epoll_create (int numDescriptors);
Единственный
параметр
numDescriptors
— это наилучшее предположение программы о том, на какое количество файловых дескрипторов будет ссылаться заново созданный дескриптор
epoll
. Это не жесткий предел, это просто подсказка ядру для более точной инициализации его внутренних структур.
epoll_create
возвращает дескриптор
epoll
, а когда программа заканчивает работу с дескриптором, его следует передать
close
, чтобы позволить ядру освободить память, используемую этим дескриптором.
Хотя дескриптор
epoll
является файловым дескриптором, его следует применять только с двумя системными вызовами.
#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents,
int timeout);
Большинство этих параметров используют структуру
struct epoll_event
, которая определяется, как показано ниже.
#include <sys/epoll.h>
struct epoll_event {
int events;
union {
void * ptr;
int fd;
unsigned int u32;
unsigned long long u64;
} data;
};
Эта структура обслуживает три цели: определяет, какие типы событий следует проверять, определяет типы произошедших событий и ассоциирует отдельный элемент данных с файловым дескриптором. Поле
events
предназначено для первых двух функций и является одной или несколькими перечисленными далее значениями, объединенными с помощью логического "ИЛИ" [80] .
80
EPOLLET
— это еще одно значение, которое может иметь
events
, переключающее
epoll
с запуска уровнем на запуск фронтом сигнала. Эта тема выходит за рамки настоящей книги, и
epoll
, запускаемую фронтом, следует применять только в особых случаях.
EPOLLIN
Определяет, что операция
read
не блокируется; данные или уже готовы, или их уже не осталось для считывания.
EPOLLOUT
Связанный файл готов для записи.
EPOLLPRI
Файл имеет внешние данные, готовые для чтения.
Второй элемент
struct epoll_event, data
, представляет собой объединение, содержащее целое число (для хранения файлового дескриптора), указатель, а также 32- и 64-битные целые числа [81] . Этот элемент данных хранится в
epoll
и возвращается в программу всякий раз, когда происходит событие подходящего типа. Элемент
data
— это единственный способ, с помощью которого программе нужно выяснить, какой файловый дескриптор необходимо обслужить; интерфейс
epoll
не передает файловый дескриптор программе, в отличие от
poll
и
select
(если
data
не содержит файловый дескриптор). Этот метод обеспечивает дополнительную гибкость приложениям, которые отслеживают файлы как нечто, более сложное, чем простые файловые дескрипторы.
81
Структура, показанная в тексте, предоставляет правильные размеры элементов на большинстве платформ, но они неправильны для машин, в которых
int
имеет 64 бита.
Системный вызов
epoll_ctl
добавляет файловые дескрипторы к набору, на который
ссылается дескриптор
epfdepoll
, и удаляет их из него.
Второй параметр,
op
, описывает, каким образом следует модифицировать набор файловых дескрипторов, и является одним из перечисленных ниже.
EPOLL_CTL_ADD
Файловый дескриптор
fd
добавляется к набору файловых дескрипторов набором событий
events
. Если файловый дескриптор уже присутствует, он возвращает
EEXIST
. (Несколько потоков могут добавлять тот же файловый дескриптор к набору
epoll
более одного раза, но это действие ничего не меняет.)
EPOLL_CTL_DEL
Файловый дескриптор
fd
удаляется из контролируемого набора файловых дескрипторов. Параметр
events
должен указывать на
struct epoll_event
, но содержимое этой структуры игнорируется. (Это еще раз доказывает, что
events
должен быть допустимым указателем; он не может быть
NULL
.)
EPOLL_CTL_MOD
Системный вызов
struct epoll_event
для
fd
обновляется на основе информации, на которую указывает
events
. Это позволяет контролировать набор событий и обновлять элемент данных, ассоциируемый с файловым дескриптором, не создавая условий состязания.
Последним системным вызовом
epoll
является
epoll_wait
, который блокирует до тех пор, пока один или несколько контролируемых файловых дескрипторов не будут иметь данные для чтения или же не будут готовы к записи. Первым аргументом является дескриптор
epoll
, а последний — тайм-аутом в секундах. Если файловые дескрипторы не готовы к обработке до истечения тайм-аута,
epoll_wait
возвращает
0
.
Два промежуточных параметра определяют буфер для ядра, в который можно копировать структуры
struct epoll_event
. Параметр
events
указывает на буфер,
maxevents
определяет, какое количество структур
struct epoll_event
помещается в буфер, а возвращаемое значение сообщает программе количество структур, помещенных в этот буфер (пока вызов не попадет в состояние тайм-аута либо не произойдет ошибка).
Каждый системный вызов
struct epoll_event
сообщает программе полное состояние контролируемого файлового дескриптора. Элемент
events
может иметь установленные флаги
EPOLLIN
,
EPOLLOUT
или
EPOLLPRI
, а также два новых флага, которые описаны ниже.
EPOLLERR
С файлом связано ожидающее состояние ошибки; это случается, если ошибка происходит в сокете, когда приложение не считывает из него или не записывает в него.
EPOLLHUP
Файловый дескриптор завис; в главе 10 дана информация о том, когда это обычно происходит.
На первый взгляд это все может показаться сложным, но на самом деле это очень похоже на работу
poll
. Вызов
epoll_create
— это то же, что и распределение массива
struct pollfd
, a
epoll_ctl
— это то же, что и инициализация элементов этого массива. Главный цикл, обрабатывающий файловые дескрипторы, использует