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

ИМЯ

       timer_create - создаёт таймер POSIX для определённого процесса

LIBRARY

       Real-time library (librt, -lrt)

СИНТАКСИС

       #include <signal.h>           /* определения констант SIGEV_* */
       #include <time.h>

       int timer_create(clockid_t clockid,
                        struct sigevent *_Nullable restrict sevp,
                        timer_t *restrict timerid);

   Требования макроса тестирования свойств для glibc (см. feature_test_macros(7)):

       timer_create():
           _POSIX_C_SOURCE >= 199309L

ОПИСАНИЕ

       Вызов  timer_create()  создаёт  новый  таймер  для  процесса. Идентификатор нового таймера возвращается в
       буфере, указанном в timerid, его значение не должно быть равно null. Данный  идентификатор  уникален  для
       процесса, пока таймер не будет удалён. Новый таймер создаётся неактивным.

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

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

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

       CLOCK_PROCESS_CPUTIME_ID (начиная с Linux 2.6.12)
              Часы, измеряющие время ЦП (пользовательское и системное), затраченное вызывающим процессом  (всеми
              его нитями).

       CLOCK_THREAD_CPUTIME_ID (начиная с Linux 2.6.12)
              Часы, измеряющие время ЦП (пользовательское и системное), затраченное вызывающей нитью.

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

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

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

       CLOCK_TAI (начиная с Linux 3.10)
              A system-wide clock derived from wall-clock time but ignoring leap seconds.

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

       Помимо  значений,  перечисленных  ранее,  в  clockid  может  быть  указано  clockid, возвращённое вызовом
       clock_getcpuclockid(3) или pthread_getcpuclockid(3).

       Аргумент sevp указывает  на  структуру  sigevent,  которая  задаёт  способ  уведомления  вызывающего  при
       срабатывании таймера. Определение и описание структуры смотрите в sigevent(7).

       В поле sevp.sigev_notify можно указать следующие значения:

       SIGEV_NONE
              Выполнять синхронное уведомление при срабатывании таймера. Ход таймера можно отслеживать с помощью
              timer_gettime(2).

       SIGEV_SIGNAL
              При  срабатывании  таймера  генерировать  для  процесса сигнал sigev_signo. Подробности смотрите в
              sigevent(7). Полю si_code структуры siginfo_t присваивается  значение  SI_TIMER.  В  любой  момент
              времени  для  таймера  в очередь процесса ставится не более одного сигнала; подробности смотрите в
              timer_getoverrun(2).

       SIGEV_THREAD
              При срабатывании вызвать sigev_notify_function, как если бы это была начальная функция новой нити.
              Подробности смотрите в sigevent(7).

       SIGEV_THREAD_ID (есть только в Linux)
              Как для SIGEV_SIGNAL, но сигнал нацелен на нить,  чей  ID  указывается  в  sigev_notify_thread_id,
              который  должен  быть  нитью  того  же  процесса  что  и вызывающий. В поле sigev_notify_thread_id
              указывается ID ядерной нити, то есть значение, возвращаемое  clone(2)  или  gettid(2).  Этот  флаг
              предназначен только для использования в библиотеках нитей.

       Указание  в  sevp  значения  NULL  эквивалентно  указанию  указателя  на  структуру  sigevent,  в которой
       sigev_notify равно SIGEV_SIGNAL, sigev_signo равно SIGALRM и sigev_value.sival_int равно ID таймера.

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

       При успешном выполнении timer_create() возвращается 0 и ID нового  таймера  помещается  в  *timerid.  При
       ошибке возвращается -1, а errno устанавливается в соответствующее значение.

ОШИБКИ

       EAGAIN Временная ошибка, на время выделения ядром структур таймера.

       EINVAL Некорректный ID часов, sigev_notify, sigev_signo или sigev_notify_thread_id.

       ENOMEM Невозможно выделить память.

       ENOTSUP
              The kernel does not support creating a timer against this clockid.

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

ВЕРСИИ

       Данный системный вызов появился в Linux 2.6.

СТАНДАРТЫ

       POSIX.1-2001, POSIX.1-2008.

ЗАМЕЧАНИЯ

       С помощью timer_create() программа может создавать несколько интервальных таймеров.

       Таймеры не наследуются в потомке после fork(2), и выключаются и удаляются при execve(2).

       Ядро  заранее  выделяет  «сигнал  реального  времени  в  очереди»  для  каждого   таймера,   создаваемого
       timer_create().  В  результате,  количество  таймеров  ограничено  ресурсом  RLIMIT_SIGPENDING  (смотрите
       setrlimit(2)).

       Таймеры,  созданные  timer_create(),  часто  называют  «(интервальными)  таймерами  POSIX».   Программный
       интерфейс таймеров POSIX состоит из следующих интерфейсов:

       timer_create()
              Create a timer.

       timer_settime(2)
              Arm (start) or disarm (stop) a timer.

       timer_gettime(2)
              Fetch  the time remaining until the next expiration of a timer, along with the interval setting of
              the timer.

       timer_getoverrun(2)
              Return the overrun count for the last timer expiration.

       timer_delete(2)
              Disarm and delete a timer.

       Начиная с Linux 3.10, файл /proc/pid/timers можно использовать для просмотра списка  таймеров  POSIX  для
       процесса с PID равным pid. Подробности смотрите в proc(5).

       Начиная  с Linux 4.10, поддержка таймеров POSIX теперь необязательна и включена по умолчанию. Поддержку в
       ядре можно выключить через параметр CONFIG_POSIX_TIMERS.

   Отличия между библиотекой C и ядром
       Частично, реализация программного интерфейса таймеров POSIX предоставляется glibc. А именно:

       •  Большая часть функций для SIGEV_THREAD реализована в glibc, а не в ядре (это  необходимо,  так  как  в
          обработку  уведомления  вовлечена  нить,  которая  должна  управляться библиотекой C, реализующей нити
          POSIX). Хотя уведомление доставляется процессу через нить, внутри реализации NPTL для  SIGEV_THREAD_ID
          используется  значение  sigev_notify и сигнал реального времени, который зарезервирован для реализации
          (смотрите nptl(7)).

       •  Стандартная ситуация, когда evp  равно  NULL,  обрабатывается  в  glibc,  где  вызывается  нижележащий
          системный вызов с заполненной подходящим образом структурой sigevent.

       •  Идентификаторы   таймеров,  обрабатываемые  на  уровне  пользователя,  поддерживаются  glibc,  которая
          отображает эти ID в ID таймеров, созданных ядром.

       The POSIX timers system calls first appeared in Linux 2.6.  Prior to this, glibc provided  an  incomplete
       user-space  implementation  (CLOCK_REALTIME  timers only) using POSIX threads, and before glibc 2.17, the
       implementation falls back to this technique on systems running kernels older than Linux 2.6.

ПРИМЕРЫ

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

       В следующем примере программа спит 1 секунду после создания таймера,  который  работает  с  частотой  100
       наносекунд. За время разблокировки и доставки сигнала, произошло около 10 миллионов переполнений.

           ./$ ./a.out 1 100
           Устанавливается обработчик сигнала 34
           Блокируется сигнал 34
           ID таймера — 0x804c008
           Спим 1 секунду
           Разблокируется сигнал 34
           Пойман сигнал 34
               sival_ptr = 0xbfb174f4;     *sival_ptr = 0x804c008
               счётчик переполнения = 10004886

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

       #include <signal.h>
       #include <stdint.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <time.h>
       #include <unistd.h>

       #define CLOCKID CLOCK_REALTIME
       #define SIG SIGRTMIN

       #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                               } while (0)

       static void
       print_siginfo(siginfo_t *si)
       {
           int      or;
           timer_t  *tidp;

           tidp = si->si_value.sival_ptr;

           printf("    sival_ptr = %p; ", si->si_value.sival_ptr);
           printf("    *sival_ptr = %#jx\n", (uintmax_t) *tidp);

           or = timer_getoverrun(*tidp);
           if (or == -1)
               errExit("timer_getoverrun");
           else
               printf("    счётчик переполнения = %d\n", or);
       }

       static void
       handler(int sig, siginfo_t *si, void *uc)
       {
           /* Замечание: вызов printf() из обработчика сигнала небезопасен
              (и не должен выполняться в готовых программах), так как
              printf() не async-signal-safe; смотрите signal-safety(7).
              Тем не менее, здесь мы используем printf(), так как это простой
              способ показать когда вызывается обработчик. */

           printf("Пойман сигнал %d\n", sig);
           print_siginfo(si);
           signal(sig, SIG_IGN);
       }

       int
       main(int argc, char *argv[])
       {
           timer_t            timerid;
           sigset_t           mask;
           long long          freq_nanosecs;
           struct sigevent    sev;
           struct sigaction   sa;
           struct itimerspec  its;

           if (argc != 3) {
               fprintf(stderr, "Использование: %s <secs> <nsecs>\n",
                       argv[0]);
               exit(EXIT_FAILURE);
           }

           /* Устанавливаем обработчик для сигнала таймера. */

           printf("Устанавливается обработчик сигнала %d\n", SIG);
           sa.sa_flags = SA_SIGINFO;
           sa.sa_sigaction = handler;
           sigemptyset(&sa.sa_mask);
           if (sigaction(SIG, &sa, NULL) == -1)
               errExit("sigaction");

           /* Временно блокируем сигнал таймера. */

           printf("Блокируется сигнал %d\n", SIG);
           sigemptyset(&mask);
           sigaddset(&mask, SIG);
           if (sigprocmask(SIG_SETMASK, &mask, NULL) == -1)
               errExit("sigprocmask");

           /* Создаём таймер. */

           sev.sigev_notify = SIGEV_SIGNAL;
           sev.sigev_signo = SIG;
           sev.sigev_value.sival_ptr = &timerid;
           if (timer_create(CLOCKID, &sev, &timerid) == -1)
               errExit("timer_create");

           printf("ID таймера %#jx\n", (uintmax_t) timerid);

           /* Запускаем таймер. */

           freq_nanosecs = atoll(argv[2]);
           its.it_value.tv_sec = freq_nanosecs / 1000000000;
           its.it_value.tv_nsec = freq_nanosecs % 1000000000;
           its.it_interval.tv_sec = its.it_value.tv_sec;
           its.it_interval.tv_nsec = its.it_value.tv_nsec;

           if (timer_settime(timerid, 0, &its, NULL) == -1)
                errExit("timer_settime");

           /* Ненадолго засыпаем; за это время, таймер может сработать
              несколько раз. */

           printf("Спим %d секунду\n", atoi(argv[1]));
           sleep(atoi(argv[1]));

           /* Разблокируем сигнал таймера, чтобы доставлялись
              уведомления таймера. */

           printf("Разблокируется сигнал %d\n", SIG);
           if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
               errExit("sigprocmask");

           exit(EXIT_SUCCESS);
       }

СМ. ТАКЖЕ

       clock_gettime(2), setitimer(2), timer_delete(2), timer_getoverrun(2), timer_settime(2),
       timerfd_create(2), clock_getcpuclockid(3), pthread_getcpuclockid(3), pthreads(7), sigevent(7), signal(7),
       time(7)

ПЕРЕВОД

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

       Этот перевод является бесплатной документацией; прочитайте Стандартную общественную лицензию GNU версии 3
       или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.

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

Linux man-pages 6.03                            5 февраля 2023 г.                                timer_create(2)