Provided by: manpages-ru-dev_4.21.0-2_all bug

ИМЯ

       timerfd_create,  timerfd_settime,  timerfd_gettime  - таймеры, уведомляющие через файловые
       дескрипторы

LIBRARY

       Standard C library (libc, -lc)

СИНТАКСИС

       #include <sys/timerfd.h>

       int timerfd_create(int clockid, int flags);

       int timerfd_settime(int fd, int flags,
                           const struct itimerspec *new_value,
                           struct itimerspec *_Nullable old_value);
       int timerfd_gettime(int fd, struct itimerspec *curr_value);

ОПИСАНИЕ

       Эти системные вызовы создают и работают с таймером, доставляя уведомления  о  срабатывании
       через    файловый    дескриптор.   Они   предоставляют   альтернативу   setitimer(2)   или
       timer_create(2); их преимущество в том, что  за  файловым  дескриптором  можно  следить  с
       помощью select(2), poll(2) и epoll(7).

       Использование данных трёх системных вызовов аналогично timer_create(2), timer_settime(2) и
       timer_gettime(2) (аналога timer_getoverrun(2) нет, так как его  возможности  предоставляет
       read(2) как описано ниже).

   timerfd_create()
       Вызов  timerfd_create()  создаёт  новый  объект  таймера и возвращает файловый дескриптор,
       который ссылается на этот таймер. В аргументе clockid задаются часы, которые  используются
       для хода таймера; значением должно быть одно из следующих:

       CLOCK_REALTIME
              Настраиваемые системные часы реального времени.

       CLOCK_MONOTONIC
              Ненастраиваемые,  постоянно  идущие  вперёд  часы,  отсчитывающие время с некоторой
              неопределённой точки в прошлом, которая не изменяется с момент запуска системы.

       CLOCK_BOOTTIME (начиная с Linux 3.15)
              Подобно CLOCK_MONOTONIC, представляет монотонно растущие часы.  Однако,  если  часы
              CLOCK_MONOTONIC  не  отсчитывают время когда система находится в состоянии ожидания
              (suspended), а часы CLOCK_BOOTTIME учитывают время в таком состоянии  системы.  Это
              полезно    приложениям,    которым   необходимо   учитывать   состояние   ожидания.
              CLOCK_REALTIME не подходят для  таких  приложений,  так  как  эти  часы  подвержены
              скачкообразным изменениям системных часов.

       CLOCK_REALTIME_ALARM (начиная с Linux 3.11)
              Эти  часы  подобны  CLOCK_REALTIME,  но  разбудят  систему,  если  она  находится с
              состоянии ожидания. Для установки таймера по этим  часам  вызывающий  должен  иметь
              мандат CAP_WAKE_ALARM.

       CLOCK_BOOTTIME_ALARM (начиная с Linux 3.11)
              Эти  часы  подобны  CLOCK_BOOTTIME,  но  разбудят  систему,  если  она  находится с
              состоянии ожидания. Для установки таймера по этим  часам  вызывающий  должен  иметь
              мандат CAP_WAKE_ALARM.

       See clock_getres(2)  for some further details on the above clocks.

       Текущее значение каждого из этих часов можно получить с помощью clock_gettime(2).

       Начиная  с  Linux  2.6.27,  для  изменения  поведения  timerfd_create() можно использовать
       следующие значения flags (через OR):

       TFD_NONBLOCK  Устанавливает флаг состояния файла O_NONBLOCK для нового открытого файлового
                     описания (смотрите open(2)), на которое ссылается новый файловый дескриптор.
                     Использование данного флага делает ненужными дополнительные вызовы  fcntl(2)
                     для достижения того же результата.

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

       До Linux 2.6.26 включительно аргумент flags должен быть равен нулю.

   timerfd_settime()
       Вызов  timerfd_settime() запускает или останавливает таймер, на который ссылается файловый
       дескриптор fd.

       The new_value argument specifies the initial expiration and interval for the  timer.   The
       itimerspec structure used for this argument is described in itimerspec(3type).

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

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

       По умолчанию, начальное время срабатывания, задаваемое в new_value, считается относительно
       текущего  времени  часов  таймера  на  момент вызова (т. е., в new_value.it_value задаётся
       время относительно текущего значения часов, заданных в clockid). Использование  абсолютной
       задержки можно включить через аргумент flags.

       Аргумент flags является битовой маской, которая может включать следующие значения:

       TFD_TIMER_ABSTIME
              Считать  new_value.it_value  абсолютным  значением часов таймера. Таймер сработает,
              когда значение часов таймера достигнет значения, указанного в new_value.it_value.

       TFD_TIMER_CANCEL_ON_SET
              Если этот флаг указан вместе с TFD_TIMER_ABSTIME и часами  этого  таймера  являются
              CLOCK_REALTIME  или  CLOCK_REALTIME_ALARM,  то помечать этот таймер как отменяемый,
              если   часы   реального    времени    подвергнуться    скачкообразному    изменению
              (settimeofday(2),   clock_settime(2)   или   подобными).   Когда   такие  изменения
              происходят,  текущий  или  будущий  вызова  read(2)   для   файлового   дескриптора
              завершается с ошибкой ECANCELED.

       Если  old_value  не  равно  NULL,  то  это  указатель  на структуру itimerspec, и он будет
       использоваться для возврата текущих на момент вызова настроек таймера;  смотрите  описание
       timerfd_gettime() далее.

   timerfd_gettime()
       Вызов   timerfd_gettime()   возвращает   в  curr_value,  которое  указывает  на  структуру
       itimerspec, текущие настройки таймера, на который ссылается файловый дескриптор fd.

       В поле it_value возвращается время до следующего срабатывания таймера. Если оба поля  этой
       структуры  равны  нулю,  то  таймер  в  данный момент не запущен. Это поле всегда содержит
       относительное значение, независимо от того,  был  ли  указан  флаг  TFD_TIMER_ABSTIME  при
       настройке таймера.

       В поле it_interval возвращается интервал таймера. Если оба поля этой структуры равны нулю,
       то таймер настроен на однократное срабатывание, на время, заданное в curr_value.it_value.

   Работа с файловым дескриптором таймера
       The file descriptor  returned  by  timerfd_create()   supports  the  following  additional
       operations:

       read(2)
              If  the  timer  has  already expired one or more times since its settings were last
              modified using timerfd_settime(), or since the last successful  read(2),  then  the
              buffer  given to read(2)  returns an unsigned 8-byte integer (uint64_t)  containing
              the number of expirations that have occurred.  (The returned value is in host  byte
              order—that is, the native byte order for integers on the host machine.)

              Если  таймер  ещё не срабатывал до вызова read(2), то вызов блокирует выполнение до
              следующего срабатывания таймера, или завершается с ошибкой  EAGAIN,  если  файловый
              дескриптор был создан неблокирующим (с помощью вызова fcntl(2) и операции F_SETFL с
              флагом O_NONBLOCK).

              Вызов read(2) завершается ошибкой  EINVAL,  если  размер  указанного  буфера  будет
              меньше 8 байт.

              Если  используются  часы  CLOCK_REALTIME  или CLOCK_REALTIME_ALARM, таймер является
              абсолютным  (TFD_TIMER_ABSTIME)  и  при  вызове   timerfd_settime()   указан   флаг
              TFD_TIMER_CANCEL_ON_SET,  то  read(2)  завершается  ошибкой  ECANCELED,  если  часы
              реального времени подвергнуться скачкообразному изменению (это позволяет  читающему
              приложению обнаружить такие скачкообразные изменения часов).

              If the associated clock is either CLOCK_REALTIME or CLOCK_REALTIME_ALARM, the timer
              is absolute (TFD_TIMER_ABSTIME),  and  the  flag  TFD_TIMER_CANCEL_ON_SET  was  not
              specified  when  calling timerfd_settime(), then a discontinuous negative change to
              the clock (e.g., clock_settime(2))  may cause read(2)  to  unblock,  but  return  a
              value  of  0  (i.e.,  no  bytes  read),  if  the clock change occurs after the time
              expired, but before the read(2)  on the file descriptor.

       poll(2), select(2) (и подобные)
              Файловый дескриптор доступен для чтения (в select(2) аргумент  readfds;  в  poll(2)
              флаг POLLIN), если произошло одно или более срабатываний таймера.

              Файловый  дескриптор  также  поддерживает другие мультиплексные вызовы: pselect(2),
              ppoll(2) и epoll(7).

       ioctl(2)
              Поддерживается следующая команда, относящаяся к timerfd:

              TFD_IOC_SET_TICKS (начиная с Linux 3.17)
                     Корректирует количество истечений  таймера,  которые  произошли.  Аргументом
                     является  указатель  на  ненулевое  8-байтовое целое (uint64_t*), содержащее
                     новое  количество  истечений.  После  установки  количества,  все  ожидающие
                     таймера  пробуждаются.  Единственная  цель  данной  команды  —  восстановить
                     истечений для отсечки/восстановления. Данная операция доступна только,  если
                     ядро собрано с параметром CONFIG_CHECKPOINT_RESTORE.

       close(2)
              Если файловый дескриптор больше не требуется, его нужно закрыть. Когда все файловые
              дескрипторы, связанные с одним объектом таймера, будут закрыты, таймер  выключается
              и ядро освобождает его ресурсы.

   Поведение при fork(2)
       После  fork(2) потомки наследуют копию файлового дескриптора, созданного timerfd_create().
       Файловый дескриптор потомка ссылается на тот же объект таймера, что и файловый  дескриптор
       его  родителя,  и  операция  read(2) в потомке будет возвращать информацию о срабатываниях
       таймера.

   Поведение при execve(2)
       Файловый дескриптор, созданный timerfd_create(), сохраняется при execve(2),  и  продолжает
       генерировать срабатывания таймера, если он включён.

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

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

       При успешном выполнении timerfd_settime()  и  timerfd_gettime()  возвращают  0;  в  случае
       ошибки возвращается -1, а errno устанавливается в соответствующее значение ошибки.

ОШИБКИ

       Вызов timerfd_create() может завершиться со следующими ошибками:

       EINVAL The clockid is not valid.

       EINVAL Неправильное значение flags или, для Linux 2.6.26 и старее, flags не равно 0.

       EMFILE Было  достигнуто  ограничение  по  количеству  открытых  файловых  дескрипторов  на
              процесс.

       ENFILE Достигнуто максимальное количество открытых файлов в системе.

       ENODEV Не удалось смонтировать (внутреннее) безымянное устройство inode.

       ENOMEM Недостаточно памяти ядра для создания таймера.

       EPERM  clockid was CLOCK_REALTIME_ALARM or CLOCK_BOOTTIME_ALARM but  the  caller  did  not
              have the CAP_WAKE_ALARM capability.

       Вызовы timerfd_settime() и timerfd_gettime() могут завершаться со следующими ошибками:

       EBADF  Значение fd не является правильным файловым дескриптором.

       EFAULT Некорректный указатель new_value, old_value или curr_value.

       EINVAL Значение fd не является правильным файловым дескриптором timerfd.

       Вызов timerfd_settime() также может завершиться со следующими ошибками:

       ECANCELED
              Смотрите ЗАМЕЧАНИЯ.

       EINVAL Значение new_value некорректно инициализировано (одно из tv_nsec вне диапазона от 0
              до 999999999).

       EINVAL Значение flags неверно.

ВЕРСИИ

       These system calls are available since Linux 2.6.25.  Library support  is  provided  since
       glibc 2.8.

СТАНДАРТЫ

       Данные системные вызовы есть только в Linux.

ЗАМЕЧАНИЯ

       Suppose  the  following scenario for CLOCK_REALTIME or CLOCK_REALTIME_ALARM timer that was
       created with timerfd_create():

       (1)  The timer has  been  started  (timerfd_settime())   with  the  TFD_TIMER_ABSTIME  and
            TFD_TIMER_CANCEL_ON_SET flags;

       (2)  A   discontinuous  change  (e.g.,  settimeofday(2))   is  subsequently  made  to  the
            CLOCK_REALTIME clock; and

       (3)  the caller once more calls timerfd_settime()  to rearm the timer (without first doing
            a read(2)  on the file descriptor).

       In this case the following occurs:

       •  The  timerfd_settime()   returns  -1  with  errno  set to ECANCELED.  (This enables the
          caller to know that the previous timer was affected by a discontinuous  change  to  the
          clock.)

       •  The   timer   is  successfully  rearmed  with  the  settings  provided  in  the  second
          timerfd_settime()  call.  (This was probably an implementation accident, but  won't  be
          fixed now, in case there are applications that depend on this behaviour.)

ДЕФЕКТЫ

       В  настоящее  время  timerfd_create()  поддерживает только несколько типов идентификаторов
       часов, поддерживаемых timer_create(2).

ПРИМЕРЫ

       Следующая программа создаёт таймер и затем следит за его работой.  Программа  получает  до
       трёх  аргументов  из  командной  строки.  В первом аргументе задаётся количество секунд до
       первого срабатывания таймера. Во втором аргументе задаётся интервал таймера в секундах.  В
       третьем  аргументе  задаётся  сколько  программа  должна  позволить  сработать  таймеру до
       завершения. Второй и третий аргументы необязательны.

       Следующий сеанс работы в оболочке показывает работу с программой:

           $ a.out 3 1 100
           0.000: timer started
           3.000: read: 1; total=1
           4.000: read: 1; total=2
           ^Z                  # type control-Z to suspend the program
           [1]+  Stopped                 ./timerfd3_demo 3 1 100
           $ fg                # Resume execution after a few seconds
           a.out 3 1 100
           9.660: read: 5; total=7
           10.000: read: 1; total=8
           11.000: read: 1; total=9
           ^C                  # type control-C to suspend the program

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

       #include <err.h>
       #include <inttypes.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <sys/timerfd.h>
       #include <time.h>
       #include <unistd.h>

       static void
       print_elapsed_time(void)
       {
           int                     secs, nsecs;
           static int              first_call = 1;
           struct timespec         curr;
           static struct timespec  start;

           if (first_call) {
               first_call = 0;
               if (clock_gettime(CLOCK_MONOTONIC, &start) == -1)
                   err(EXIT_FAILURE, "clock_gettime");
           }

           if (clock_gettime(CLOCK_MONOTONIC, &curr) == -1)
               err(EXIT_FAILURE, "clock_gettime");

           secs = curr.tv_sec - start.tv_sec;
           nsecs = curr.tv_nsec - start.tv_nsec;
           if (nsecs < 0) {
               secs--;
               nsecs += 1000000000;
           }
           printf("%d.%03d: ", secs, (nsecs + 500000) / 1000000);
       }

       int
       main(int argc, char *argv[])
       {
           int                fd;
           ssize_t            s;
           uint64_t           exp, tot_exp, max_exp;
           struct timespec    now;
           struct itimerspec  new_value;

           if (argc != 2 && argc != 4) {
               fprintf(stderr, "%s init-secs [interval-secs max-exp]\n",
                       argv[0]);
               exit(EXIT_FAILURE);
           }

           if (clock_gettime(CLOCK_REALTIME, &now) == -1)
               err(EXIT_FAILURE, "clock_gettime");

           /* Создаём абсолютный таймер CLOCK_REALTIME с начальным
              срабатыванием и интервалом, заданными из командной строки. */

           new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]);
           new_value.it_value.tv_nsec = now.tv_nsec;
           if (argc == 2) {
               new_value.it_interval.tv_sec = 0;
               max_exp = 1;
           } else {
               new_value.it_interval.tv_sec = atoi(argv[2]);
               max_exp = atoi(argv[3]);
           }
           new_value.it_interval.tv_nsec = 0;

           fd = timerfd_create(CLOCK_REALTIME, 0);
           if (fd == -1)
               err(EXIT_FAILURE, "timerfd_create");

           if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)
               err(EXIT_FAILURE, "timerfd_settime");

           print_elapsed_time();
           printf("таймер запущен\n");

           for (tot_exp = 0; tot_exp < max_exp;) {
               s = read(fd, &exp, sizeof(uint64_t));
               if (s != sizeof(uint64_t))
                   err(EXIT_FAILURE, "read");

               tot_exp += exp;
               print_elapsed_time();
               printf("read: %" PRIu64 "; total=%" PRIu64 "\n", exp, tot_exp);
           }

           exit(EXIT_SUCCESS);
       }

СМ. ТАКЖЕ

       eventfd(2),  poll(2),  read(2),  select(2),  setitimer(2),  signalfd(2),  timer_create(2),
       timer_gettime(2), timer_settime(2), timespec(3), epoll(7), time(7)

ПЕРЕВОД

       Русский    перевод    этой    страницы    руководства    был    сделан   Azamat   Hackimov
       <azamat.hackimov@gmail.com>,  Dmitry  Bolkhovskikh  <d20052005@yandex.ru>,   Yuri   Kozlov
       <yuray@komyakino.ru> и Иван Павлов <pavia00@gmail.com>

       Этот  перевод  является  бесплатной  документацией;  прочитайте  Стандартную  общественную
       лицензию GNU версии 3 ⟨https://www.gnu.org/licenses/gpl-3.0.html⟩ или более позднюю, чтобы
       узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.

       Если  вы  обнаружите  ошибки  в  переводе этой страницы руководства, пожалуйста, отправьте
       электронное письмо на ⟨man-pages-ru-talks@lists.sourceforge.net⟩.