Информация о готовых файловых дескрипторах возвращается в виде массива структур epoll_event, на который указывает аргумент evlist (структура epoll_event была описана в предыдущем разделе). Массив evlist выделяется вызывающим процессом, а количество содержащихся в нем элементов определяется с помощью аргумента maxevents.
Каждый элемент массива evlist возвращает информацию об отдельном готовом файловом дескрипторе. Вложенное поле events содержит маску событий, произошедших с дескриптором. Во вложенном поле data хранится значение, которое было указано внутри ev.data при регистрации нашей заинтересованности в этом конкретном дескрипторе с помощью вызова epoll_ctl(). Стоит отметить, что поле data всего лишь позволяет найти номер файлового дескриптора, связанного с текущим событием. Следовательно, во время вызова epoll_ctl(), помещающего файловый дескриптор в список интереса, мы должны либо назначить его номер полю ev.data.fd (как показано в листинге 59.3), либо присвоить ev.data.ptr указатель на структуру с данным номером.
Аргумент timeout определяет поведение вызова epoll_wait(), связанное с блокировкой:
• значение –1 приводит к блокировке до тех пор, пока в одном из дескрипторов в списке интереса для экземпляра epfd не возникнет событие или не будет перехвачен сигнал;
• значение 0 инициирует неблокирующую проверку наличия событий в файловых дескрипторах списка интереса для экземпляра epfd;
• если значение больше нуля, то вызов блокируется на timeout миллисекунд, пока в одном из дескрипторов в списке интереса для экземпляра epfd не возникнет событие или не будет перехвачен сигнал.
В случае успеха вызов epoll_wait() возвращает количество элементов, помещенных в массив evlist, или 0, если за указанный промежуток времени (timeout) не нашлось ни одного готового дескриптора. В случае ошибки epoll_wait() возвращает –1, меняя соответствующим образом значение глобальной переменной errno.
В многопоточных программах возможна ситуация, когда файловые дескрипторы, добавляемые в список интереса для экземпляра epoll одним потоком (с помощью epoll_ctl()), уже отслеживаются другим (благодаря epoll_wait()). Изменения, вносимые в список интереса, немедленно учитываются, а вызов epoll_wait() возвращает сведения о готовности файловых дескрипторов, добавленных только что.
События интерфейса epoll
Битовые значения, которые можно указать в поле ev.events при вызове epoll_ctl() и которые затем помещаются в поля evlist[].events, возвращаемые вызовом epoll_wait(), перечислены в табл. 59.8. Если не брать во внимание префикс E, большинство этих битов имеют те же имена, что и соответствующие биты событий, используемые в вызове poll(). (Исключения составляют константы EPOLLET и EPOLLONESHOT, которые чуть ниже будут описаны более подробно.) Причина такого соответствия — эти биты (неважно, передаются ли они в epoll_ctl() или возвращаются из epoll_wait()) означают то же самое, что и аналогичные биты событий вызова poll().
Таблица 59.8. Значения битовой маски поля events интерфейса epoll
Бит — Передается в epoll_ctl()? — Возвращается из epoll_wait()? — Описание
EPOLLIN — * — * — Могут быть прочитаны любые данные, кроме высокоприоритетных
EPOLLPRI — * — * — Могут быть прочитаны высокоприоритетные данные
EPOLLRDHUP — * — * — Закрытие удаленного сокета (начиная с Linux 2.6.17)
EPOLLOUT — * — * — Могут быть записаны обычные данные
EPOLLET — * — Применяются уведомления, срабатывающие по фронту
EPOLLONESHOT — * — После уведомления о событии отключается мониторинг
EPOLLERR — * — Произошла ошибка
EPOLLHUP — * Произошел разрыв соединения
Флаг EPOLLONESHOT
По умолчанию, когда файловый дескриптор добавляется в список интереса epoll с помощью epoll_ctl() EPOLL_CTL_ADD, он остается активным (то есть последующие вызовы epoll_wait() будут информировать нас о его готовности) до тех пор, пока мы вручную не удалим его из этого списка, используя операцию epoll_ctl() EPOLL_CTL_DEL. Если мы хотим получить только одно уведомление об определенном дескрипторе, то можем указать в поле ev.events вызова epoll_ctl() флаг EPOLLONESHOT (доступный начиная с Linux 2.6.2). В таком случае следующий вызов epoll_wait(), который проинформирует о готовности заданного дескриптора, будет последним (сам дескриптор в списке интереса будет помечен как неактивный). Позже при желании можно снова включить мониторинг этого файлового дескриптора, задействуя операцию epoll_ctl() EPOLL_CTL_MOD (операция EPOLL_CTL_ADD здесь не подходит, так как неактивный дескриптор по-прежнему входит в список интереса epoll).
Пример программы
Использование программного интерфейса epoll демонстрируется в листинге 59.5. В качестве аргументов командной строки эта программа принимает путь к одному или нескольким терминалам или очередям FIFO. Программа выполняет следующие действия.
• Создает экземпляр epoll