Выбрать главу

void usleep(unsigned long usec);

Функция usleep() вынуждает текущий процесс засыпать на время (в микросекундах), указанное параметром usec. Никакие сигналы не используются. На большинстве платформ usleep() реализуется с помощью select().

int select(0, NULL, NULL, NULL, struct timeval tv);

Функция select(), описанная в главе 13, предлагает мобильный способ откладывания процессов на точное количество времени. Просто введите в объект struct timeval минимальное время, которое нужно ожидать, и можете быть уверены — ни одно событие не произойдет.

int nanosleep(struct timespec *req, struct timespec *rem);

Функция nanosleep() вынуждает текущий процесс засыпать на время, указанное параметром req (описание объекта timespec можно найти в начале этой главы), пока процесс не получит сигнал. Если работа nanosleep() прекращается раньше из-за полученного сигнала, то она возвращает -1 и устанавливает для errno значение EINTR, а также, если rem не является NULL, то передает в переменную rem количество времени, оставшегося в периоде ожидания.

Функция nanosleep() наименее переносима из всех рассмотренных, поскольку она была определена как часть спецификации POSIX.1b реального времени (ранее она называлась POSIX.4), которая выполняется не во всех версиях Unix. Однако все новые реализации Unix поддерживают ее, так как функции POSIX.1b в настоящее время являются стандартной частью Single Unix Specification (Единая спецификация Unix).

Не все платформы, предусматривающие функцию nanosleep(), обеспечивают высокую точность, однако Linux, как и остальные операционные системы реального времени, стремится принимать короткие запросы на обработку с предельной точностью. Более подробную информацию о программировании в режиме реального времени можно найти в [12].

18.2.2. Интервальные таймеры

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

ITIMER_REAL Отслеживает время в терминах настенных часов — в реальном времени (в зависимости от выполнения процесса) — и генерирует сигнал SIGALRM. Несовместим с системным вызовом alarm(), который используется функцией sleep(). Не применяйте ни alarm(), ни sleep(), если имеется реальный интервальный таймер.
ITIMER_VIRTUAL Подсчитывает время только при исполнении процесса — не учитывая системные вызовы, которые производит процесс — и генерирует сигнал SIGVTALRM.
ITIMER_PROF Подсчитывает время только при выполнении процесса — включая время, за которое ядро посылает исполнительные системные вызовы от имени процесса, и не включая время, потраченное на прерывание процесса по инициативе самого процесса — и генерирует сигнал SIGPROF. Учет времени, затраченного на обработку прерываний, оказывается настолько трудоемким, что даже может изменить настройки таймера.

Комбинация таймеров ITIMER_VIRTUAL и ITIMER_PROF часто используется в профилирующих кодах.

Каждый из этих таймеров генерирует ассоциированный сигнал об истечении таймера в пределах одного хода системных часов (как правило, 1-10 миллисекунд). Если процесс работает в данное время, то сигнал генерируется сразу же; в противном случае сигнал генерируется немного позже (в зависимости от загрузки системы). Поскольку таймер ITIMER_VIRTUAL следит за временем только во время работы процесса, то сигнал всегда доставляется незамедлительно.

Используйте структуру struct itimerval для передачи запроса и установки интервальных таймеров.

struct itimerval {

 struct timeval it_interval;

 struct timeval it_value;

};

Член it_value показывает количество времени, оставшееся до отправления следующего сигнала. Член it_interval определяет время между сигналами; каждый раз при истечении таймера это значение присваивается переменной it_value.

Для взаимодействия с интервальными таймерами предусмотрены два системных вызова. Оба принимают аргумент which, указывающий обрабатываемый таймер.

int getitimer(int which, struct itimerval *val);

Переменной val присваивается текущее состояние таймера which.

int setitimer(int which, struct itimerval *new, struct itimerval *old);

Устанавливает таймер which на значение new и заменяет old предыдущей установкой, если она не равна NULL.

Если параметр таймера it_value приравнять к нулю, он немедленно заблокируется. Ввод нулевого значения для it_interval отключает таймер после следующего запуска.

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

 1: /* itimer.c */

 2:

 3: #include <stdio.h>

 4: #include <stdlib.h>

 5: #include <sys/wait.h>

 6: #include <unistd.h>

 7: #include <string.h>

 8: #include <signal.h>

 9: #include <sys/time.h>

10:

11:

12: void catch_signal(int ignored) {

13:  static int iteration=0;

14:

15:  printf("получен сигнал интервального таймера, итерация %d\n",

16:   iteration++);

17: }

18:

19: pid_t start_timer(int interval) {

20:  pid_t child;

21:  struct itimerval it;

22:  struct sigaction sa;

23:

24:  if (!(child = fork())) {

25:   memset(&sa, 0, sizeof(sa));

26:   sa.sa_handler = catch_signal;

27:   sigemptyset(&sa.sa_mask);

28:   sa.sa_flags = SA_RESTART;

29:

30:   sigaction(SIGALRM, &sa, NULL);

31:

32:   memset(&it, 0, sizeof(it));

33:   it.it_interval.tv_sec = interval;

34:   it.it_value.tv_sec = interval;

35:   setitimer(ITIMER_REAL, &it, NULL);

36:

37:   while (1) pause();

38:  }

39:

40:  return child;

41: }

42:

43: void stop_timer(pid_t child) {