• Возврат из обработчика сигнала. Предположим, что некоторый машинный код генерирует один из перечисленных сигналов, следовательно, инициируется обработчик. При нормальном возврате из обработчика программа пытается возобновить выполнение с той точки, в которой она была прервана. Однако это и есть та самая инструкция, которая сгенерировала сигнал, следовательно, сигнал генерируется повторно. Последствием такого поведения обычно является то, что программа уходит в бесконечный цикл, вновь и вновь вызывая обработчик сигнала.

Игнорирование сигнала. В игнорировании аппаратно генерируемого сигнала очень мало смысла, так как непонятно, каким образом программа должна продолжать выполнение в случае, например, арифметического исключения. При генерации одного из вышеперечисленных сигналов в результате аппаратного исключения Linux доставляет этот сигнал в программу, даже несмотря на инструкцию игнорировать такие сигналы.

Блокирование сигнала. Как и в предыдущем случае, в блокировании сигнала очень мало смысла, так как непонятно, каким образом программа должна продолжать выполнение. В Linux 2.4 и более ранних версиях ядро просто игнорирует попытки заблокировать аппаратно генерируемый сигнал. Он доставляется в процесс в любом случае, а затем либо завершает процесс, либо перехватывается обработчиком, если таковой был установлен. Начиная с Linux 2.6, если сигнал заблокирован, то процесс всегда незамедлительно аварийно завершается этим сигналом, даже если для процесса установлен обработчик данного сигнала. (Причина такого кардинального изменения в Linux 2.6 по части обработки заблокированных аппаратно генерируемых сигналов в скрытых ошибках поведения Linux 2.4, которые могли приводить к полному зависанию распоточенных программ.)

Программа signals/demo_SIGFPE.c, поставляемая вместе с файлами исходного кода к этой книге, может быть использована для демонстрации результатов игнорирования или блокирования сигнала SIGFPE либо его перехвата обработчиком, который выполняет нормальный возврат.

Правильным способом работы с аппаратно генерируемыми сигналами является либо принятие их действия по умолчанию (завершение процесса), либо написание обработчиков, которые не выполняют нормальный возврат. Вместо выполнения нормального возврата обработчик может завершить выполнение вызовом функции _exit() для завершения процесса либо вызовом функции siglongjump() (см. подраздел 21.2.1) для гарантии того, что управление передается в некую точку программы, отличную от инструкции, вызвавшей генерацию сигнала.

22.5. Синхронная и асинхронная генерация сигнала

Мы уже увидели, что процесс, как правило, не может предсказать, когда он получит сигнал. Теперь нам необходимо уточнить это наблюдение, рассмотрев различия между синхронной и асинхронной генерацией сигналов.

Модель, которую мы до сих пор неявно подразумевали, называется асинхронной генерацией сигналов. В рамках ее сигнал посылается либо другим процессом, либо генерируется ядром при свершении события, не зависящего от выполняемого процесса (например, когда пользователь вводит символ прерывания или завершается дочерний по отношению к рассматриваемому процесс). Для асинхронно генерируемых сигналов утверждение, что процесс не может предсказать, когда он получит сигнал, истинно.

Однако в некоторых случаях сигнал генерируется во время выполнения процесса. Мы уже рассмотрели два примера таких ситуаций.

• Аппаратно генерируемые сигналы (SIGBUS, SIGFPE, SIGILL, SIGSEGV и SIGEMT), описываемые в разделе 22.4, генерируются в результате выполнения конкретного машинного кода, результатом выполнения которого является аппаратное исключение.

• Процесс может использовать функции raise(), kill() или killpg() для отправки сигналов самому себе.

В вышеописанных случаях генерация сигнала синхронная — сигнал доставляется моментально (если этот сигнал не заблокирован, однако см. раздел 22.4 для получения информации о том, что происходит при блокировании аппаратно генерируемых сигналов). Иными словами, утверждение о непредсказуемости доставки сигналов в данном случае неприменимо. Для синхронно генерируемых сигналов доставка предсказуема и воспроизводима.

Обратите внимание, что синхронность — это описание того, каким образом сигнал генерируется, а не самого сигнала. Все сигналы могут быть сгенерированы синхронно (например, когда процесс отправляет самому себе сигнал kill()) или асинхронно (например, когда сигнал kill() отправлен другим процессом).

22.6. Тайминг и порядок доставки сигнала

В качестве первой темы этого раздела мы рассмотрим, когда именно доставляется ожидающий сигнал. А затем — что происходит при одновременном разблокировании нескольких ожидающих сигналов.

Когда доставляется сигнал

Перейти на страницу:

Похожие книги