alarm((argc > 1)? getInt(argv[1], GN_NONNEG, "num-secs"): 10);

numRead = read(STDIN_FILENO, buf, BUF_SIZE);

savedErrno = errno; /* На случай, если alarm() изменит errno */

alarm(0); /* Убеждаемся, что таймер выключен */

errno = savedErrno;

/* Определяем результат чтения */

if (numRead == –1) {

if (errno == EINTR)

printf("Read timed out\n");

else

errMsg("read");

} else {

printf("Successful read (%ld bytes): %.*s",

(long) numRead, (int) numRead, buf);

}

exit(EXIT_SUCCESS);

}

timers/timed_read.c

Стоит отметить наличие потенциального состояния гонки в программе из листинга 23.2. Если таймер сработает после вызова alarm(), но перед началом чтения, операция read() не будет прервана обработчиком сигнала. Но, поскольку в таких ситуациях обычно используется довольно продолжительное время ожидания (минимум несколько секунд), эта проблема крайне маловероятна, поэтому на практике данный подход является вполне разумным. В книге [Stevens & Rago, 2005] предлагается альтернативная методика на основе функции longjmp(). Еще одним вариантом работы с операциями ввода/вывода является применение системных вызовов select() или poll() (см. главу 55), которые изначально поддерживают время ожидания и позволяют отслеживать ввод-вывод сразу для нескольких дескрипторов.

23.4. Приостановка выполнения на определенный отрезок времени (переход в режим сна)

Иногда процесс нужно приостановить на определенное время. Этого можно добиться с помощью связки из вызова sigsuspend() и интерфейсов для работы с таймерами, с которыми мы только что познакомились, однако проще будет воспользоваться одной из функций перехода в режим сна.

23.4.1. Переход в режим сна (низкая точность): вызов sleep()

Функция sleep() приостанавливает выполнение вызывающего процесса на определенное количество секунд, указанное в аргументе seconds, или до момента перехвата сигнала (который прерывает данный вызов).

В момент возобновления работы вызов sleep() возвращает 0. Если вызов был прерван сигналом, он возвращает количество секунд, остававшееся до возобновления. Как и в случае с таймерами, установленными с помощью функций alarm() и setitimer(), нагрузка на систему может привести к тому, что возобновление работы процесса будет запланировано не сразу после вызова sleep(), а с некоторой (обычно небольшой) задержкой.

#include

unsigned int sleep(unsigned int seconds);

Возвращает 0 при нормальном завершении или количество секунд, остававшихся до возобновления работы, в случае преждевременного прерывания

Стандарт SUSv3 не описывает возможное взаимодействие между операцией sleep() и вызовами alarm() и setitimer(). В Linux функция sleep() реализована в виде обертки для вызова nanosleep() (см. подраздел 23.4.2), благодаря чему она никак не влияет на функции alarm() и setitimer(). Однако во многих других системах, особенно старых, операция sleep() реализована с помощью вызова alarm() и обработчика сигнала SIGALRM. При написании переносимого кода ее не следует смешивать с функциями alarm() и setitimer().

23.4.2. Переход в режим сна (высокая точность): вызов nanosleep()

Функция nanosleep() по принципу своей работы похожа на sleep(), но имеет ряд преимуществ, включая повышенную точность при задании интервала.

#define _POSIX_C_SOURCE 199309

#include

int nanosleep(const struct timespec *request, struct timespec *remain);

Возвращает 0 в случае успешного завершения или –1, если произошли ошибка или прерывание

Аргумент request обозначает продолжительность сна и является указателем на структуру следующего вида:

struct timespec {

time_t tv_sec; /* Секунды */

long tv_nsec; /* Наносекунды */

};

Поле tv_nsec содержит количество наносекунд. Это должно быть число в диапазоне от 0 до 999 999 999.

Еще одним преимуществом функции nanosleep() является то, что стандарт SUSv3 явно запрещает реализовывать ее с помощью сигналов. Это означает, что, в отличие от sleep(), ее можно использовать в сочетании с вызовом alarm() или setitimer().

Несмотря на это, функция nanosleep() тоже может быть прервана обработчиком сигнала. В этом случае она вернет –1 и присвоит переменной errno ошибку EINTR; если же аргумент remain не равен NULL, буфер, на который он указывает, будет содержать время, остававшееся до возобновления работы. При желании мы можем использовать это значение, чтобы перезапустить системный вызов и продолжить сон. Такой подход продемонстрирован в листинге 23.3. В качестве аргументов командной строки данная программа принимает секунды и наносекунды для вызова nanosleep(). Программа циклически вызывает nanosleep(), пока не завершится указанный интервал времени. Если вызов прервется обработчиком сигнала SIGINT (сгенерированного с помощью сочетания клавиш Ctrl+C), он будет перезапущен с помощью значения, возвращенного в буфере remain. Запустив эту программу, мы увидим следующее:

Перейти на страницу:

Похожие книги