/* <--- Сигнал может появиться здесь, после проверки условия! */
pause; /* pause будет вызвана в любом случае */
signal_waiting = 1;
}
/*
Определение поступившего сигнала
<--- Сигнал может переписать здесь глобальные данные */
signal_waiting = 0;
/* Обработка сигнала
<--- То же и здесь, особенно для нескольких сигналов */
}
Решением является блокирование интересующего сигнала в любое время, кроме ожидания его появления. Например, предположим, что интересующим нас сигналом является
SIGINT
:
void handler(int sig) {
/* sig автоматически блокируется функцией sigaction */
/* Установить глобальные данные, касающиеся этого сигнала */
}
int main(int argc, char **argv) {
sigset_t set;
struct sigaction act;
/* ...обычная настройка, опции процесса и т.д. ... */
sigemptyset(&set); /* Создать пустой набор */
sigaddset(&set, SIGINT); /* Добавить в набор SIGINT */
sigprocmask(SIG_BLOCK, &set, NULL); /* Заблокировать его */
act.sa_mask = set; /* Настроить обработчик */
act.sa_handler = handler;
act.sa_flags = 0;
sigaction(sig, &act, NULL); /* Установить его */
... /* Возможно, установить отдельные обработчики */
... /* для других сигналов */
sigemptyset(&set); /* Восстановить пустой, допускает SIGINT */
for (;;) {
sigsuspend(&set); /* Ждать появления SIGINT */
/* Обработка сигнала. SIGINT здесь снова блокируется */
}
/* ...любой другой код... */
return 0;
}
Ключом к использованию этого является то, что
sigsuspend
временно заменяет маску сигналов процесса маской, переданной в аргументе. Это дает
SIGINT
возможность появиться. При появлении он обрабатывается; обработчик сигнала возвращается, а вслед за ним возвращается также
sigsuspend
.
Ко времени возвращения
sigsuspend
первоначальная маска процесса снова на месте.
Вы легко можете расширить этот пример для нескольких сигналов, блокируя в
main
и в обработчике все интересующие сигналы и разблокируя их лишь в вызове
sigsuspended
.
При наличии всего этого не следует в новом коде использовать
pause
.
pause
был стандартизован POSIX главным образом для поддержки старого кода. То же самое верно и для функции
sigpause
System V Release 3. Вместо этого, если нужно структурировать свое приложение с использованием сигналов для IPC, используйте исключительно функции API
sigsuspend
и
sigaction
.
ЗАМЕЧАНИЕ. Приведенный выше код предполагает, что маска сигналов процесса начинается пустой. Код изделия должен вместо этого работать с любой маской сигналов, имеющейся на момент запуска программы.
10.8. Важные сигналы специального назначения
Некоторые сигналы имеют особое назначение. Здесь мы опишем наиболее важные.
10.8.1. Сигнальные часы:
sleep
,
alarm
и
SIGALARM
Часто бывает необходимо написать программу в виде
while (/* некоторое неверное условие */) {
/* подождать некоторое время */
}
Часто такая потребность возникает в сценариях оболочки, например, в ожидании регистрации определенного пользователя:
until who | grep '^arnold' > /dev/null
do
sleep 10
done
Два механизма, один низкоуровневый, другой высокоуровневый, позволяют работающему процессу узнать, когда истекло заданное количество секунд.
10.8.1.1. Труднее, но с большим контролем:
alarm
и
SIGALARM
Основным строительным блоком является системный вызов
alarm
:
#include <unistd.h> /* POSIX */
unsigned int alarm(unsigned int seconds);
После того, как
alarm
возвратится, программа продолжает работать. Однако, когда истекают
seconds
секунд, ядро посылает процессу
SIGALARM
. Действием по умолчанию является завершение процесса, но вы скорее всего вместо этого установите обработчик сигнала для
SIGALARM
.
Возвращаемое значение либо 0, либо, если был установлен предыдущий сигнальный интервал, число секунд, остающихся до его завершения. Однако, для процесса имеется лишь один такой сигнальный интервал; предыдущий отменяется, а новый помещается на его место.