Сигналы играют важную роль в различных частях API системных вызовов, и мы еще вернемся к ним в последующих главах. Кроме того, некоторые функции, связанные с сигналами, также характерны для потоков (например, pthread_kill() и pthread_sigmask()), но мы отложим обсуждение этих функций до раздела 33.2.
См. ресурсы, перечисленные в разделе 20.15.
22.1. В разделе 22.2 отмечалось, что если остановленный процесс, установивший обработчик для заблокированного сигнала SIGCONT, в дальнейшем возобновляется в результате получения сигнала SIGCONT, то обработчик активируется только после разблокирования сигнала SIGCONT. Напишите программу, демонстрирующую это поведение. Не забудьте, что процесс может быть остановлен путем ввода в терминал символа
22.2. Если сигнал реального времени и стандартный сигнал ожидают процесс, стандарт SUSv3 не устанавливает, который из сигналов должен быть доставлен первым. Напишите программу, демонстрирующую, что делает Linux в этом случае. (С помощью программы установите обработчик для всех сигналов, заблокируйте сигналы на определенное время, чтобы вы могли отправить в программу несколько сигналов, а затем разблокируйте все сигналы.)
22.3. В разделе 22.10 сказано, что прием сигналов с помощью функции sigwaitinfo() осуществляется быстрее по сравнению с использованием обработчика сигнала с функцией sigsuspend(). В программе signals/sig_speed_sigsuspend.c, поставляемой с исходным кодом для этой книги, задействуется функция sigsuspend() для реализации обмена сигналами между родительским и дочерним процессами. Засеките время выполнения этой программы при обмене одним миллионом сигналов между двумя процессами. (Количество сигналов, которыми необходимо обменяться, указывается в аргументе командной строки программы.) Создайте модифицированную версию программы, в которой будет использоваться функция sigwaitinfo(), и засеките время выполнения этой версии программы. Какова разница в скоростях выполнения этих программ?
23. Таймеры и переход в режим сна
Таймер позволяет процессу планировать появление уведомлений, которые должны поступать в будущем. Переход в режим сна дает возможность процессу (или потоку) приостанавливать выполнение на определенный период времени. В данной главе описываются интерфейсы для этих механизмов. Мы рассмотрим следующие темы:
• классические программные интерфейсы UNIX для установки интервальных таймеров (setitimer() и alarm()), срабатывание которых приводит к уведомлению процесса;
• программные интерфейсы, которые позволяют процессу приостанавливать выполнение на определенном отрезке времени;
• программные интерфейсы системных часов и таймеров, входящие в стандарт POSIX.1b;
• механизм timerfd, доступный только в Linux, который позволяет создавать таймеры, чье срабатывание можно определить по файловому дескриптору.
Системный вызов setitimer() устанавливает
#include
int setitimer(int
struct itimerval *
Возвращает 0 при успешном завершении или –1, если случилась ошибка
С помощью вызова setitimer() процесс может устанавливать таймеры трех разных видов, в зависимости от выбранного значения which:
• ITIMER_REAL — создает таймер, который ведет обратный отсчет в реальном времени (то есть так, как на обычных настенных часах). По истечении времени процессу передается сигнал SIGALRM;
• ITIMER_VIRTUAL — создает таймер, который ведет обратный отсчет в виртуальном времени процесса (то есть учитывается процессорное время в пользовательском режиме). По истечении времени процессу передается сигнал SIGVTALRM;
• ITIMER_PROF — создает профилирующий таймер, который ведет обратный отсчет с учетом времени процесса (суммируя процессорное время в пользовательском режиме и в режиме ядра). По истечении времени процессу передается сигнал SIGPROF.
Стандартное действие всех сигналов, которые генерируются таймерами, состоит в завершении процесса. Если нам нужно сделать что-то другое, мы должны установить собственный обработчик сигнала.
Аргументы new_value и old_value представляют собой указатели на структуры itimerval, определенные следующим образом:
struct itimerval {
struct timeval it_interval; /* Временной отрезок для интервального таймера */
struct timeval it_value; /* Текущее значение (время
до следующего срабатывания) */
};
Каждое из полей структуры itimerval является, в свою очередь, структурой типа timeval, содержащей секунды и микросекунды:
struct timeval {
time_t tv_sec; /* Секунды */
suseconds_t tv_usec; /* Микросекунды (long int) */
};