The website "dmilvdv.narod.ru." is not registered with uCoz.
If you are absolutely sure your website must be here,
please contact our Support Team.
If you were searching for something on the Internet and ended up here, try again:

About uCoz web-service

Community

Legal information

7.3.7 Время и таймеры POSIX.1b

7.3.7 Время и таймеры POSIX.1b

Предыдущая  Содержание  Следующая V*D*V

Традиционные обычные таймеры UNIX в Linux, функции setitimer и getitimer, не отвечают требованиям большинства приложений реального времени. Таймеры POSIX.1b имеют следующие преимущества по сравнению с обычными таймерами UNIX.

 

Процесс может иметь несколько таймеров.

Лучшая точность таймеров. Таймеры могут использовать значения с точностью до наносекунд.

Уведомление о завершении работы таймера может быть сделано либо с помощью любого произвольного сигнала (реального времени) или с помощью потоков. Для уведомление о завершении работы таймера в обычных таймерах UNIX есть лишь ограниченный набор сигналов.

Таймеры POSIX.1b предоставляют поддержку различных часов, таких как CLOCK_REALTIME, CLOCK_MONOTONIC, и так далее, которые могут иметь различные источники с различным разрешением. Обычный таймер UNIX, с другой стороны, связан с системными часами.

 

Ядро таймеров POSIX.1b представляет собой набор часов, которые используются как привязка ко времени. Linux обеспечивает поддержку следующих часов:

 

CLOCK_REALTIME: общесистемные часы реального времени, видимые для всех процессов, работающих в системе. Часы измеряют количество времени в секундах и наносекундах с начала эпохи (то есть 00:00:00 1 января 1970 по Гринвичу).Разрешение часов равно 1/HZ секунд. Таким образом, если HZ равно 100, то разрешение часов составляет 10 мс. Если HZ равно 1000, то разрешение часов составляет 1 мс. Чтобы узнать значение HZ в вашей системе, посмотрите, пожалуйста, файл <kernel-source>/include/asm/param.h. Так как это время базируются на времени настенных часов, оно может быть изменено.

CLOCK_MONOTONIC: время непрерывной работы системы, видимое всем процессам в системе. В Linux оно измеряется  как количество времени в секундах и наносекундах после загрузки системы. Его разрешение равно 1/HZ с. Его поддержка доступна начиная с ядра версии 2.5 и glibc 2.3.3. Это время не может быть изменено каким-либо процессом.

CLOCK_PROCESS_CPUTIME_ID: часы, измеряющие время работы процесса. Время текущего процесса, потраченное на выполнение в системе, измеряется в секундах и наносекундах. Разрешение равно 1/HZ. Это время может быть изменено.

CLOCK_THREAD_CPUTIME_ID: То же, что и CLOCK_PROCESS_CPUTIME_ID, но для текущего потока.

 

Обычно CLOCK_REALTIME используется для указания абсолютного времени ожидания. CLOCK_MONOTONIC используется для относительного времени ожидания и периодических задач. Поскольку время этих часов не может быть изменено, периодическим задачам не нужно беспокоиться о преждевременном или задержанном пробуждении, которое могло бы произойти с CLOCK_REALTIME. Двое других часов могут использоваться для целей учёта. Интерфейсы времени и таймеров POSIX.1b  перечислены в Таблице 7.10.

 

Таблица 7.10 Функции времени и таймеров POSIX.1b

 

Метод

Описание

clock_settime

Установка значения указанным часам.

clock_gettime

Получение времени.

clock_getres

Получение разрешения (точности) часов.

clock_nanosleep

Приостановка выполнения вызывающего приложения на указанное время.

timer_create

Создание таймера на основе указанного времени.

timer_delete

Удаление таймера.

timer_settime

Установка времени работы таймера.

timer_gettime

Получение текущего значения таймера.

timer_getoverrun

Возвращает число, показывающее сколько раз таймер закончил работу между моментом генерации сигнала и его доставкой

 

Объясним использование описанных выше интерфейсов на примере. В этом примере мы создаём таймер POSIX, основанный на CLOCK_MONOTONIC. Это периодический таймер с периодом четыре секунды. По окончании работы таймера происходит уведомление процесса с помощью сигнала реального времени SIGRTMIN. Процесс зарегистрировал обработчик сигнала для SIGRTMIN, который хранит счётчик числа раз завершения работы таймера. Когда счётчик достигает заданного значения, названного в примере как MAX_EXPIRE, таймер останваливается и процесс завершается.

 

#include <unistd.h>

#include <time.h>

#include <signal.h>

 

#define MAX_EXPIRE 10

int expire;

 

void timer_handler(int signo, siginfo_t *info,

                   void *context);

 

int main(){

  struct timespec ts, tm, sleep;

  sigset_t mask;

  siginfo_t info;

  struct sigevent sigev;

  struct sigaction sa;

  struct itimerspec ival;

  timer_t tid;

 

Сначала распечатаем некоторые статистические данные о CLOCK_MONOTONIC. clock_getres даёт разрешение часов, а clock_gettime даёт время работы системы. Обратите внимание, что разрешение CLOCK_MONOTONIC равно 1/HZ.

 

clock_getres(CLOCK_MONOTONIC, &ts);

clock_gettime(CLOCK_MONOTONIC, &tm);

printf("CLOCK_MONOTONIC res: [%d]sec [%d]nsec/n",

                          ts.tv_sec, ts.tv_nsec);

printf("system up time: [%d]sec [%d]nsec\n",

                          tm.tv_sec, tm.tv_nsec);

 

Настраиваем обработчик сигнала для SIGRTMIN. Как упоминалось ранее, по истечении времени работы таймера процесс получит сигнал реального времени SIGRTMIN.

 

/* Мы не хотим никаких блокированных сигналов */

sigemptyset(&mask);

sigprocmask(SIG_SETMASK, &mask, NULL);

 

/* Регистрируем обработчик для SIGRTMIN */

sa.sa_flags = SA_SIGINFO;

sigemptyset(&sa.sa_mask);

sa.sa_sigaction = timer_handler;

if (sigaction(SIGRTMIN, &sa, NULL) == -1) {

  perror("sigaction failed");

  return -1;

}

 

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

 

/*

 * По завершении работы таймера должен быть послан

 * сигнал SIGRTMIN с произвольным значением 1

 */

sigev.sigev_notify = SIGEV_SIGNAL;

sigev.sigev_signo = SIGRTMIN;

sigev.sigev_value.sival_int = 1;

 

/*

 * Создаём таймер. Обратите внимание, что если вызов

 * успешен, идентификатор таймера возвращается в

 * третьем аргументе.

 */

if (timer_create(CLOCK_MONOTONIC, &sigev, &tid) == -1){

  perror("timer_create");

  return -1;

}

printf("timer-id = %d\n", tid);

 

Взводим таймер. Время истечёт через пять секунд и после каждых четырёх секунд впоследствии.

 

ival.it_value.tv_sec = 5;

ival.it_value.tv_nsec = 0;

ival.it_interval.tv_sec = 4;

ival.it_interval.tv_nsec = 0;

if (timer_settime(tid, 0, &ival, NULL) == -1){

  perror("timer_settime");

  return -1;

}

 

Наконец, ждём истечения времени работы таймера. Если счётчик числа раз окончания работы таймера достигает MAX_EXPIRE, выключаем таймер и выходим.

 

  /* Спим и ждём сигнал */

  for(;;){

    sleep.tv_sec = 3;

    sleep.tv_nsec = 0;

    clock_nanosleep(CLOCK_MONOTONIC, 0, &sleep, NULL);

    printf("woken up\n");

    if (expire >= MAX_EXPIRE){

      printf("Program quitting.\n");

      /*

       * Если it_value == 0, вызываем timer_settime,

       * чтобы отключить таймер

       */

      memset(&ival, 0, sizeof (ival));

      timer_settime(tid, 0, &ival, NULL);

      return 0;

    }

  }

  return 0;

}

 

Наконец, мы имеем timer_handler: вспомните наше обсуждение обработчиков сигналов из Раздела 7.3.6. Второй аргумент обработчика имеет тип siginfo_t, который содержит информацию относительно принимаемого сигнала. В этом случае значением info->si_code является SI_TIMER.

 

void timer_handler(int signo, siginfo_t *info,

                   void *context)

{

  int overrun;

  printf("signal details: signal (%d), code (%d)\n",

                      info->si_signo, info->si_code);

  if (info->si_code == SI_TIMER){

    printf("timer-id = %d \n", info->si_timerid);

    expire++;

 

    /*

     * Спецификация говорит, что в один момент времени

     * для данного таймера только один экземпляр сигнала

     * помещается в очередь процесса. Если таймер, сигнал

     * которого всё еще ожидает доставки, заканчивает

     * работу, сигнал не помещается в очередь и происходит

     * ситуация переполнения таймера. timer_getoverrun

     * возвращает дополнительное число окончаний работы

     * таймера, которые произошли между моментом, когда

     * был сгенерирован сигнал (помещён в очередь) и

     * моментом, когда он был доставлен или принят

     */

    if ((overrun = timer_getoverrun(info->si_timerid)) !=

                                  -1 && overrun != 0){

      printf("timer overrun %d\n", overrun);

      expire += overrun;

    }

  }

}

 

Таймеры высокого разрешения

 

Для часов, о которых говорилось выше, Linux обеспечивает наилучшее разрешение в 1 мс (HZ = 1000). Этого разрешения не достаточно для большинства приложений реального времени, так как они требуют точности порядка микросекунд и наносекунд. Для поддержки таких приложений инженерами MontaVista был начат проект High-Resolution Timers (HRT), Таймеры Высокого Разрешения. HRT являются таймерами POSIX с разрешением до микросекунды. Введены двое дополнительных часов POSIX, CLOCK_REALTIME_HR и CLOCK_MONOTONIC_HR. Они такие же, как их коллеги, не обладающие высокой точностью; отличие в том, что разрешение времени у них имеет порядок микросекунд или наносекунд, в зависимости от генератора частоты для аппаратных часов. На момент написания, поддержка HRT не включена в основное дерево исходных текстов и доступна в виде патча. Более подробно о проекте можно узнать на www.sourceforge.net/projects/high-res-timers.

 

Что следует помнить

 

Точность часов является фиксированной и не может быть изменена во время выполнения приложения.

Чтобы деактивировать таймер, вызовите timer_settime со значением члена it_value структуры itimespec, равным нулю.

Таймер может быть периодическим или однократным. Если член it_interval структуры itimerspec во время вызова timer_setime равен нулю, то таймер однократный; в противном случае он является периодическим.

POSIX.1b также обеспечивает функцию nanosleep. Она такая же, как и clock_nanosleep, с CLOCK_REALTIME в качестве первого аргумента.

Таймеры, предназначенные для каждого процесса, не наследуются дочерним процессом.

 

Предыдущая  Содержание  Следующая