$ ./t_nanosleep 10 0
Slept for: 1.853428 secs
Remaining: 8.146617000
Slept for: 4.370860 secs
Remaining: 5.629800000
Slept for: 6.193325 secs
Remaining: 3.807758000
Slept for: 10.008150 secs
Sleep complete
Функция nanosleep() позволяет указывать продолжительность сна в наносекундах, одна ее реальная точность ограничена программными часами (см. раздел 10.6). Если указать интервал, который не является кратным программным часам, он будет округлен в большую сторону.
Как отмечалось ранее, в системах, поддерживающих высокоточные таймеры, точность сна может быть намного лучше, чем у программных часов.
Наличие округления означает, что, если сигналы принимают с высокой частотой, у нас могут возникнуть проблемы с подходом, который используется в программе из листинга 23.3. Дело в том, что при каждом повторном запуске nanosleep() будет накапливаться отклонение, связанное с округлением, так как вероятность того, что итоговое значение remain окажется кратным наименьшему интервалу программных часов, довольно низкая. Следовательно, каждый следующий вызов nanosleep() будет останавливать выполнение на более продолжительное время, чем указано в значении remain предыдущего вызова. В случае если сигналы доставляются с высокой частотой (то есть чаще, чем обновляются программные часы), процесс может никогда не выйти из состояния сна. В Linux 2.6 и выше эту проблему можно обойти с помощью вызова clock_nanosleep() и параметра TIMER_ABSTIME, которые будут рассмотрены в подразделе 23.5.4.
Листинг 23.3. Использование функции nanosleep()
timers/t_nanosleep.c
#define _POSIX_C_SOURCE 199309
#include
#include
#include
#include "tlpi_hdr.h"
static void
sigintHandler(int sig)
{
return; /* Просто прерываем nanosleep() */
}
int
main(int argc, char *argv[])
{
struct timeval start, finish;
struct timespec request, remain;
struct sigaction sa;
int s;
if (argc!= 3 || strcmp(argv[1], "-help") == 0)
usageErr("%s secs nanosecs\n", argv[0]);
request.tv_sec = getLong(argv[1], 0, "secs");
request.tv_nsec = getLong(argv[2], 0, "nanosecs");
/* Позволяем обработчику SIGINT прерывать nanosleep() */
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = sigintHandler;
if (sigaction(SIGINT, &sa, NULL) == –1)
errExit("sigaction");
if (gettimeofday(&start, NULL) == –1)
errExit("gettimeofday");
for (;;) {
s = nanosleep(&request, &remain);
if (s == –1 && errno!= EINTR)
errExit("nanosleep");
if (gettimeofday(&finish, NULL) == –1)
errExit("gettimeofday");
printf("Slept for: %9.6f secs\n", finish.tv_sec — start.tv_sec +
(finish.tv_usec — start.tv_usec) / 1000000.0);
if (s == 0)
break; /* Вызов nanosleep() завершен */
printf("Remaining: %2ld.%09ld\n", (long) remain.tv_sec, remain.tv_nsec);
request = remain; /* Следующий переход в режим сна будет
длиться оставшееся время */
}
printf("Sleep complete\n");
exit(EXIT_SUCCESS);
}
timers/t_nanosleep.c
POSIX-часы (изначально разработанные для стандарта POSIX.1b) предоставляют программный интерфейс для доступа к часам, которые измеряют время в наносекундах. Для представления такого времени используется та же структура timespec, которую мы применяли в подразделе 23.4.2 в вызове nanosleep().
В Linux программы, которые работают с этим интерфейсом, должны быть скомпилированы с параметром — lrt, иначе их нельзя будет скомпоновать с библиотекой реального времени librt.
Главными системными вызовами программного интерфейса POSIX-часов являются clock_gettime(), который возвращает текущее значение часов, clock_getres(), позволяющий определить их точность, и clock_settime(), который обновляет часы.
23.5.1. Получение текущего значения часов: вызов clock_gettime()
Системный вызов clock_gettime() возвращает время в соответствии с часами, указанными в аргументе clockid.
#define _POSIX_C_SOURCE 199309
#include
int clock_gettime(clockid_t
int clock_getres(clockid_t
Оба вызова возвращают 0 при успешном завершении или –1, если случилась ошибка
На значение времени, возвращаемое внутри структуры timespec, указывает аргумент tp. И хотя структура timespec поддерживает наносекунды, значение, возвращенное вызовом clock_gettime(), может оказаться менее точным. Системный вызов clock_getres() возвращает указатель на структуру timespec (аргумент clockid), в которой содержится точность часов.
Тип данных clockid_t предусмотрен стандартом SUSv3 для представления идентификатора часов. Значения, которые можно указывать в аргументе clockid, перечислены в первом столбце табл. 23.1.
Таблица 23.1. Типы часов стандарта POSIX.1b
Идентификатор часов — Описание