Provided by: manpages-ru-dev_4.19.0-7_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 ⟨https://www.gnu.org/licenses/gpl-3.0.html⟩ или более позднюю, чтобы
       узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.

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