Linux программирование в примерах
Типичная высокоуровневая структура основанного на сигналах приложения выглядит таким образом:
for(;;){/* <i>Ожидание сигнала</i> *//* <i>Обработка сигнала</i> */}Оригинальным интерфейсом V7 для ожидания сигнала является
:pause()#include <unistd.h> /* POSIX */int pause(void);приостанавливает процесс; она возвращается лишь после того, как сигнал будет доставлен и его обработчик вернется из вызова. По определению,pause()полезна лишь с перехваченными сигналами — игнорируемые сигналы при их появлении игнорируются, а сигналы с действием по умолчанию, завершающим процесс (с созданием файла образа или без него), продолжают действовать так же.pause()Проблема в только что описанной высокоуровневой структуре приложения кроется в части «Обработка сигнала». Когда этот код запускается, вы не захотите обрабатывать другой сигнал; вы хотите завершить обработку текущего сигнала до перехода к следующему. Одним из возможных решений является структурирование обработчика сигнала таким образом, что он устанавливает флаг и проверяет его в главном цикле:
volatile sig_atomic_t signal_waiting = 0; /* true, если не обрабатываются сигналы */void handler(int sig) {signal_waiting = 1;/* Установка других данных, указывающих вид сигнала */В основном коде флаг проверяется:
for (;;) {if (!signal_waiting) { /* Если возник другой сигнал, */pause(); /* этот код пропускается */signal_waiting = 1;}/* Определение поступившего сигнала */signal_waiting = 0;/* Обработка сигнала */}К сожалению, этот код изобилует условиями гонки:
for (;;) {if (!signal_waiting) {/* <--- Сигнал может появиться здесь, после проверки условия! */pause(); /* pause() будет вызвана в любом случае */signal_waiting = 1;}/* Определение поступившего сигнала<--- Сигнал может переписать здесь глобальные данные */signal_waiting = 0;/* Обработка сигнала<--- То же и здесь, особенно для нескольких сигналов */}Решением является блокирование интересующего сигнала в любое время, кроме ожидания его появления. Например, предположим, что интересующим нас сигналом является
:SIGINTvoid 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()был стандартизован POSIX главным образом для поддержки старого кода. То же самое верно и для функцииpause()System V Release 3. Вместо этого, если нужно структурировать свое приложение с использованием сигналов для IPC, используйте исключительно функции APIsigpause()иsigsuspend().sigaction()ЗАМЕЧАНИЕ. Приведенный выше код предполагает, что маска сигналов процесса начинается пустой. Код изделия должен вместо этого работать с любой маской сигналов, имеющейся на момент запуска программы.
10.8. Важные сигналы специального назначения
Некоторые сигналы имеют особое назначение. Здесь мы опишем наиболее важные.