Хотя в предыдущем моем блоге сигналы были перечислены в качестве методов IPC, чаще всего они используются в широком разнообразии других контекстов, поэтому заслуживают более подробного рассмотрения.
Сигналы зачастую описываются как «программные прерывания». Поступление сигнала информирует процесс о том, что случилось какое-то событие или возникли исключительные условия. Существует множество разнообразных сигналов, каждый из которых идентифицирует событие или условие. Каждый тип сигнала идентифицируется с помощью целочисленного значения, определяемого в символьном имени, имеющем форму SIGxxxx
.
Сигналы отправляются процессу ядром, другим процессом (с соответствующими разрешениями) или самим процессом. Например, ядро может отправить сигнал процессу, когда произойдет что-нибудь из следующего перечня:
- пользователь набрал на клавиатуре команду прерывания (обычно это
Ctrl+C
); - завершился один из дочерних процессов данного процесса;
- истекло время таймера (будильника), установленного процессом;
- процесс попытался получить доступ к неверному адресу в памяти.
В оболочке сигнал процессу можно отправить с помощью команды kill
. Внутри программ ту же возможность может предоставить системный вызов kill()
.
Когда процесс получает сигнал, он, в зависимости от сигнала, выполняет одно из следующих действий:
- игнорирует сигнал;
- прекращает свою работу по сигналу;
- приостанавливается, чтобы впоследствии возобновить свое выполнение с получением сигнала специального назначения.
Для большинства типов сигналов вместо выполнения исходного действия по сигналу программа может либо проигнорировать сигнал (что пригодится, если игнорирование не является исходной реакцией на сигнал), либо установить обработчик сигнала. Последний представляет собой функцию, определенную программистом, которая автоматически вызывается при доставке сигнала процессу. Эта функция выполняет некоторые действия, из-за которых был сгенерирован сигнал.
В период времени между генерированием сигнала и его доставкой сигнал для процесса считается ожидающим. Обычно ожидающий сигнал доставляется сразу же, как только получающий его процесс будет спланирован следующим для выполнения, или немедленно, если процесс уже выполняется. Но можно также заблокировать сигнал, добавив его в маску сигналов процесса. Если сигнал был сгенерирован после блокировки, он остается ожидающим до тех пор, пока в последующем блокировка не будет снята (например, удалена из маски сигналов).