timer_create(2)

ИМЯ
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 pre-2.6 Linux kernels.

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

В следующем примере программа спит 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.