| TIMER_CREATE(2) | Руководство программиста Linux | TIMER_CREATE(2) | 
timer_create - создаёт таймер POSIX для определённого процесса
#include <signal.h> #include <time.h>
int timer_create(clockid_t clockid, struct sigevent *sevp,
                 timer_t *timerid);
Компонуется при указании параметра -lrt.
Требования макроса тестирования свойств для glibc (см. feature_test_macros(7)):
timer_create(): _POSIX_C_SOURCE >= 199309L
Вызов timer_create() создаёт новый таймер для процесса. Идентификатор нового таймера возвращается в буфере, указанном в timerid, его значение не должно быть равно null. Данный идентификатор уникален для процесса, пока таймер не будет удалён. Новый таймер создаётся неактивным.
В аргументе clockid задаются часы, которые используются в новом таймере для учёта времени. Это может быть одно из следующих значений:
Помимо значений, перечисленных ранее, в clockid может быть указано clockid, возвращённое вызовом clock_getcpuclockid(3) или pthread_getcpuclockid(3).
Аргумент sevp указывает на структуру sigevent, которая задаёт способ уведомления вызывающего при срабатывании таймера. Определение и описание структуры смотрите в sigevent(7).
В поле sevp.sigev_notify можно указать следующие значения:
Указание в sevp значения NULL эквивалентно указанию указателя на структуру sigevent, в которой sigev_notify равно SIGEV_SIGNAL, sigev_signo равно SIGALRM и sigev_value.sival_int равно ID таймера.
При успешном выполнении timer_create() возвращается 0 и ID нового таймера помещается в *timerid. При ошибке возвращается -1, а errno устанавливается в соответствующее значение.
Данный системный вызов появился в 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 состоит из следующих интерфейсов:
Начиная с Linux 3.10, файл /proc/[pid]/timers можно использовать для просмотра списка таймеров POSIX для процесса с PID равным pid. Подробности смотрите в proc(5).
Начиная с Linux 4.10, поддержка таймеров POSIX теперь необязательна и включена по умолчанию. Поддержку в ядре можно выключить через параметр CONFIG_POSIX_TIMERS.
Частично, реализация программного интерфейса таймеров POSIX предоставляется glibc. А именно:
Системные таймерные вызовы POSIX впервые появились в Linux 2.6. До этого в glibc была неполная реализация в пространстве пользователя (только таймеры CLOCK_REALTIME) с использованием нитей POSIX, а реализация glibc до версии 2.17 переключается на неё в системах с ядрами до 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 <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <time.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)
{
    timer_t *tidp;
    int or;
    tidp = si->si_value.sival_ptr;
    printf("    sival_ptr = %p; ", si->si_value.sival_ptr);
    printf("    *sival_ptr = 0x%lx\n", (long) *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;
    struct sigevent sev;
    struct itimerspec its;
    long long freq_nanosecs;
    sigset_t mask;
    struct sigaction sa;
    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 таймера — 0x%lx\n", (long) 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)
| 2019-03-06 | Linux |