EVENTFD(2) Руководство программиста Linux EVENTFD(2)

ИМЯ

eventfd - создаёт файловый дескриптор для уведомления о событиях

ОБЗОР

#include <sys/eventfd.h>

int eventfd(unsigned int initval, int flags);

ОПИСАНИЕ

Вызов eventfd() создаёт «объект eventfd», который можно использовать в качестве механизма ожидания/уведомления о событиях в приложениях пространства пользователя и ядра. Объект содержит беззнаковое 64-битный (uint64_t) счётчик, обслуживаемый ядром. Этот счётчик инициализируется значением, указанным в аргументе initval.

При завершении работы eventfd() возвращает новый файловый дескриптор, который можно использовать для ссылки на объект eventfd.

Для изменения поведения eventfd() можно использовать следующие значения flags (через OR):

Устанавливает флаг close-on-exec (FD_CLOEXEC) для нового открытого файлового дескриптора. Смотрите описание флага O_CLOEXEC в open(2) для того, чтобы узнать как это может пригодиться.
Устанавливает флаг состояния файла O_NONBLOCK для нового открытого файлового описания (смотрите open(2)), на которое ссылается новый файловый дескриптор. Использование данного флага делает ненужными дополнительные вызовы fcntl(2) для достижения того же результата.
Предоставляет семафоро-подобную семантику для чтения из нового файлового дескриптора. Смотрите ниже.

До версии Linux 2.6.26 аргумент flags не использовался, и должен быть равен нулю.

Следующие операции могут выполняться над полученным файловым дескриптором eventfd():

read(2)
Каждый завершившийся без ошибок вызов read(2) возвращает 8-байтное целое. Вызов read(2) завершается с ошибкой EINVAL, если размер указанного буфера меньше 8 байт.
Возвращаемое read(2) значение имеет порядок байт узла, т. е., используемый порядок байт для целых на машине узла.
Семантика read(2) зависит от значения счётчика eventfd — равно оно нулю или нет, и был ли указан флаг EFD_SEMAPHORE при создании файлового дескриптора eventfd:
  • Если флаг EFD_SEMAPHORE не указан и счётчик eventfd не равен нулю, то read(2) возвращает 8 байт с его значением и значение счётчика сбрасывается в ноль.
  • Если флаг EFD_SEMAPHORE задан указан и счётчик eventfd не равен нулю, то read(2) возвращает 8 байт, содержащие значение 1, и значение счётчика уменьшается на 1.
  • Если счётчик eventfd равен нулю во время вызова read(2), то вызов блокируется до тех пор, пока счётчик станет не равным нулю (время работы read(2) описано выше) или завершается с ошибкой EAGAIN, если файловый дескриптор создан неблокируемым.
write(2)
При вызове write(2) из его буфера к счётчику добавляется 8-байтовое целое значение. Максимальное значение, которое может храниться в счётчике, равно наибольшему 64-битному беззнаковому значению минус 1 (т.е., 0xfffffffffffffffe). Если при добавлении значение счётчика превысит максимум, то write(2) заблокируется до тех пор, пока для файлового дескриптора не будет выполнен вызов read(2), или завершится с ошибкой EAGAIN, если файловый дескриптор создан неблокируемым.
Вызов write(2) завершается с ошибкой EINVAL, если размер указанного буфера меньше 8 байт, или если попытаться записать значение 0xffffffffffffffff.
poll(2), select(2) (и подобные)
Возвращённый файловый дескриптор поддерживает poll(2) (и, аналогично, epoll(7)) и select(2) следующим образом:
  • Файловый дескриптор доступен для чтения (в select(2) аргумент readfds; в poll(2) флаг POLLIN), если счётчик больше 0.
  • Файловый дескриптор доступен для записи (в select(2) аргумент writefds; в poll(2) флаг POLLOUT), если можно записать значение равное, как минимум, "1" без блокировки.
  • Если обнаружено переполнение счётчика, то select(2) указывает на файловый дескриптор, доступный на чтение и запись, и poll(2) возвращает событие POLLERR. Как упоминалось ранее, write(2) никогда не может вызвать переполнение счётчика. Однако переполнение может произойти, если подсистемой KAIO выполнится (возможно теоретически) 2^64 "передач сигналов" eventfd. Если переполнение произошло, то read(2) вернёт максимальное значение типа uint64_t (т.е., 0xffffffffffffffff).
Файловый дескриптор eventfd также поддерживает другие мультиплексные программные интерфейсы: pselect(2) и ppoll(2).
close(2)
Если файловый дескриптор больше не требуется, его нужно закрыть. Когда все файловые дескрипторы, связанные с одним объектом eventfd, будут закрыты, ядро освобождает ресурсы объекта.

Копия файлового дескриптора, созданного eventfd(), наследуется потомком, созданным с помощью fork(2). Копия файлового дескриптора связывается с тем же объектом eventfd. Файловые дескрипторы, созданные eventfd(), сохраняются при вызове execve(2), если не указан флаг close-on-exec.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

При успешном выполнении eventfd() возвращает новый файловый дескриптор eventfd. При ошибке возвращается -1, и errno устанавливается в соответствующее значение.

ОШИБКИ

В flags указано неподдерживаемое значение.
Было достигнуто ограничение по количеству открытых файловых дескрипторов на процесс.
Достигнуто максимальное количество открытых файлов в системе.
Не удалось смонтировать (внутреннее) безымянное устройство inode.
Недостаточно памяти для создания нового файлового дескриптора eventfd.

ВЕРСИИ

Вызов eventfd() доступен в Linux начиная с ядра 2.6.22. Поддержка в glibc появилась в версии 2.8. Системный вызов eventfd2() (см. ЗАМЕЧАНИЯ) доступен в Linux начиная с ядра 2.6.27. В glibc версии 2.9 в обёртке eventfd() используется системный вызов eventfd2(), если он поддерживается ядром.

АТРИБУТЫ

Описание терминов данного раздела смотрите в attributes(7).

Интерфейс Атрибут Значение
eventfd() Безвредность в нитях MT-Safe

СООТВЕТСТВИЕ СТАНДАРТАМ

Вызовы eventfd() и eventfd2() есть только в Linux.

ЗАМЕЧАНИЯ

Приложения могут использовать файловый дескриптор eventfd вместо канала (см. pipe(2)) во всех случаях, когда канал используется только для сигнализации о событиях. Издержки ядра по файловому дескриптору eventfd намного меньше, чем по каналу и требуется только один файловый дескриптор (против двух, при использовании канала).

При использовании в ядре файловый дескриптор eventfd может предоставлять мост из ядерного в пользовательское пространство, позволяя например работать, подобно KAIO (ядерный AIO), сигнализируя, что завершена какая-то операция над файловым дескриптором.

Важным моментом файлового дескриптора eventfd является то, что за ним можно следить как за обычным файловым дескриптором с помощью select(2), poll(2) или epoll(7). Это означает, что приложение может одновременно отслеживать готовность "обычных" файлов и готовность других механизмов ядра, которые поддерживают интерфейс eventfd. (Без интерфейса eventfd() эти механизмы невозможно мультиплексировать через select(2), poll(2) или epoll(7).)

Текущее значение счётчика eventfd можно найти в записи для соответствующего файлового дескриптора в каталоге процесса /proc/[pid]/fdinfo. Подробности смотрите в proc(5).

Отличия между библиотекой C и ядром

Основу составляют два системных вызова Linux: eventfd() и более новый eventfd2(). В первом системном вызове не реализован аргумент flags. Последний системный вызов использует значения flags, которые были описаны ранее. Обёрточная функция glibc использует eventfd2(), если он доступен.

Дополнительные возможности glibc

В библиотеке GNU C определён дополнительный тип и две функции, которые пытаются устранить сложности чтения и записи из файлового дескриптора eventfd:

typedef uint64_t eventfd_t;
int eventfd_read(int fd, eventfd_t *value);
int eventfd_write(int fd, eventfd_t value);

Функции выполняют операции чтения и записи из файлового дескриптора eventfd, и возвращают 0, если передано правильное количество байт и -1 в противном случае.

ПРИМЕР

Следующая программа создаёт файловый дескриптор eventfd и затем создаёт дочерний процесс. Пока родительский процесс на короткое время засыпает, потомок пишет все числа, переданные в командной строке программы, в файловый дескриптор eventfd. Когда родитель просыпается, он читает их из файлового дескриптора eventfd.

Пример сеанса работы с программой:

$ ./a.out 1 2 4 7 14
Child writing 1 to efd
Child writing 2 to efd
Child writing 4 to efd
Child writing 7 to efd
Child writing 14 to efd
Child completed write loop
Parent about to read
Parent read 28 (0x1c) from efd

Исходный код программы

#include <sys/eventfd.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>             /* определение uint64_t */
#define handle_error(msg) \

    do { perror(msg); exit(EXIT_FAILURE); } while (0)
int
main(int argc, char *argv[])
{

    int efd, j;

    uint64_t u;

    ssize_t s;

    if (argc < 2) {

        fprintf(stderr, "Использование: %s <num>...\n", argv[0]);

        exit(EXIT_FAILURE);

    }

    efd = eventfd(0, 0);

    if (efd == -1)

        handle_error("eventfd");

    switch (fork()) {

    case 0:

        for (j = 1; j < argc; j++) {

            printf("Child writing %s to efd\n", argv[j]);

            u = strtoull(argv[j], NULL, 0);

                    /* в strtoull() разрешены различные основания */

            s = write(efd, &u, sizeof(uint64_t));

            if (s != sizeof(uint64_t))

                handle_error("write");

        }

        printf("Child completed write loop\n");

        exit(EXIT_SUCCESS);

    default:

        sleep(2);

        printf("Parent about to read\n");

        s = read(efd, &u, sizeof(uint64_t));

        if (s != sizeof(uint64_t))

            handle_error("read");

        printf("Parent read %llu (0x%llx) from efd\n",

                (unsigned long long) u, (unsigned long long) u);

        exit(EXIT_SUCCESS);

    case -1:

        handle_error("fork");

    }
}

СМОТРИТЕ ТАКЖЕ

futex(2), pipe(2), poll(2), read(2), select(2), signalfd(2), timerfd_create(2), write(2), epoll(7), sem_overview(7)

2019-03-06 Linux