Читать книгу 📗 "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()SIGINTsigsuspend()sigsuspend()Вы легко можете расширить этот пример для нескольких сигналов, блокируя в
main()sigsuspended()При наличии всего этого не следует в новом коде использовать
pause()pause()sigpause()sigsuspend()sigaction()ЗАМЕЧАНИЕ. Приведенный выше код предполагает, что маска сигналов процесса начинается пустой. Код изделия должен вместо этого работать с любой маской сигналов, имеющейся на момент запуска программы.
10.8. Важные сигналы специального назначения
Некоторые сигналы имеют особое назначение. Здесь мы опишем наиболее важные.