Книга: Программирование для Linux. Профессиональный подход
3.3. Сигналы
3.3. Сигналы
Сигналы — это механизм связи между процессами в Linux. Данная тема очень обширна, поэтому здесь мы рассмотрим лишь наиболее важные сигналы и методики управления процессами.
Сигнал представляет собой специальное сообщение, посылаемое процессу. Сигналы являются асинхронными: когда процесс принимает сигнал, он немедленно обрабатывает его, прерывая выполнение текущей функции и даже текущей строки программы. Есть несколько десятков различных сигналов, каждый из которых имеет свое функциональное назначение. Все они распознаются по номерам, но в программах для ссылки на сигналы пользуются символическими константами. В Linux эти константы определены в файле /usr/include/bits/signum.h (его не нужно включать в программы, для этого есть файл <signal.h>).
В ответ на полученный сигнал процесс выполняет ряд действий в зависимости от типа сигнала. У каждого сигнала есть стандартный обработчик, определяющий, что произойдет с процессом, если он попытается проигнорировать сигнал. Для большинства сигналов можно также задавать явную функцию обработки. В этом случае при поступлении сигнала выполнение программы приостанавливается, выполняется обработчик, а потом программа возобновляет свою работу.
Операционная система Linux посылает процессам сигналы в случае возникновения определенных ситуаций. Например, сигналы SIGBUS
(ошибка на шине), SIGSEGV
(нарушение сегментации) и SIGFPE
(ошибка операции с плавающей запятой) могут быть посланы процессу, пытающемуся выполнить неправильную операцию. По умолчанию эти сигналы приводят к завершению процесса и созданию дампа оперативной памяти.
Процесс может сам послать сигнал другому процессу. Чаще всего возникает необходимость завершить требуемый процесс с помощью сигнала SIGTERM
или SIGKILL
.[12] С помощью сигналов можно также передавать команды выполняющимся программам. Для этого существуют "пользовательские" сигналы SIGUSR1
и SIGUSR2
. Иногда в аналогичных целях применяется сигнал SIGHUP
, с помощью которого можно заставить программу повторно прочитать свои файлы конфигурации.
Функция sigaction()
определяет правила обработки указанного сигнала. Первый ее аргумент — это номер сигнала. Следующие два аргумента представляют собой указатели на структуру sigaction
; первый из них регистрирует новый обработчик сигнала, а второй содержит описание предыдущего обработчика. Наиболее важным полем структуры sigaction
является sa_handler
. Оно может содержать одно из трех значений:
? SIG_DFL
— выбор стандартного обработчика сигнала;
? SIG_IGN
— игнорирование сигнала,
? указатель на функцию обработки сигнала; эта функция должна принимать один параметр (номер сигнала) и возвращать значение типа void
.
Поскольку сигнал может прийти в любой момент, он способен застать программу "врасплох" за выполнением критической операции, не подразумевающей прерывание. Такой операцией, к примеру, является обработка предыдущего сигнала. Отсюда правило: следует избегать операций ввода-вывода и вызовов большинства библиотечных и системных функций в обработчиках сигналов.
Обработчик должен выполнять минимум действий в ответ на получение сигнала и как можно быстрее возвращать управление в программу (или просто завершать ее работу). В большинстве случаев обработчик просто фиксирует факт поступления сигнала, а основная программа периодически проверяет, был ли сигнал, и реагирует должным образом.
Тем не менее возможность прерывания обработчика никогда нельзя исключать. Это очень сложная ситуация для диагностирования и отладки (и наглядный пример состояния гонки, о котором пойдет речь в разделе 4.4. "Синхронизация потоков и критические секции"). Необходимо внимательно следить за тем, что именно делается в обработчике.
Даже присвоение значения глобальной переменной несет потенциальную опасность, так как данная операция может занять два или три такта процессора, а за это время успеет прийти следующий сигнал, вследствие чего переменная окажется поврежденной. Если обработчик использует какую-то переменную в качестве флага поступления сигнала, она должна иметь специальный тип sig_atomic_t
. Linux гарантирует, что операция присваивания значения такой переменной займет ровно один такт и не будет прервана. На самом деле тип sig_atomic_t
в Linux эквивалентен типу int
; более того, операции присваивания целочисленных переменных (32- и 16-разрядных) и указателей всегда атомарны. Использовать тип sig_atomic_t
необходимо для того, чтобы программу можно было перенести в любую стандартную UNIX-систему.
В листинге 3.5 представлен шаблон программы, в которой функция-обработчик подсчитывает, сколько раз программа получает сигнал SIGUSR1
.
- Звуковые сигналы BIOS
- 12.6. Сигналы реального времени
- 7.2.6.3. Системные демоны и традиционные сигналы
- Сигналы и шумы
- Световые сигналы
- Сигналы
- Глава 10 Сигналы
- 10.3. Стандартные сигналы С: signal() и raise()
- 10.6. Сигналы POSIX
- 10.7. Сигналы для межпроцессного взаимодействия
- 10.8. Важные сигналы специального назначения
- 10.8.2. Сигналы, управляющие заданиями