Понятие о надежности сигналов. POSIX функции для работы с сигналами
Основным недостатком системного вызова signal() является его низкая надежность.
Во многих вариантах операционной системы UNIX установленная при его помощи обработка сигнала пользовательской функцией выполняется только один раз, после чего автоматически восстанавливается реакция на сигнал по умолчанию. Для постоянной пользовательской обработки сигнала необходимо каждый раз заново устанавливать реакцию на сигнал прямо внутри функции-обработчика.
В системных вызовах и пользовательских программах могут существовать критические участки, на которых процессу недопустимо отвлекаться на обработку сигналов. Мы можем выставить на этих участках реакцию "игнорировать сигнал" с последующим восстановлением предыдущей реакции, но если сигнал все-таки возникнет на критическом участке, то информация о его возникновении будет безвозвратно потеряна.
Наконец, последний недостаток связан с невозможностью определения количества сигналов одного и того же типа, поступивших процессу, пока он находился в состоянии готовность. Сигналы одного типа в очередь не ставятся! Процесс может узнать о том, что сигнал или сигналы определенного типа были ему переданы, но не может определить их количество. Этот недостаток мы можем проиллюстрировать, слегка изменив программу с асинхронным получением информации о статусе завершившихся процессов, рассмотренную нами ранее в разделе "Изучение особенностей получения терминальных сигналов текущей и фоновой группой процессов". Пусть в новой программе 13–14-6.c процесс-родитель порождает в цикле пять новых процессов, каждый из которых сразу же завершается со своим собственным кодом, после чего уходит в бесконечный цикл.
Листинг 13-14.6. Программа (13–14-6.c) для иллюстрации ненадежности сигналов. (html, txt)
Сколько сообщений о статусе завершившихся детей мы ожидаем получить? Пять! А сколько получим? It depends... Откомпилируйте, прогоните и посчитайте.
Последующие версии System Y и BSD пытались устранить эти недостатки собственными средствами.
Единый способ более надежной обработки сигналов появился с введением POSIX стандарта на системные вызовы UNIX. Набор функций и системных вызовов для работы с сигналами был существенно расширен и построен таким образом, что позволял временно блокировать обработку определенных сигналов, не допуская их потери. Однако проблема, связанная с определением количества пришедших сигналов одного типа, по-прежнему остается актуальной. (Надо отметить, что подобная проблема существует на аппаратном уровне и для внешних прерываний. Процессор зачастую не может определить, какое количество внешних прерываний с одним номером возникло, пока он выполнял очередную команду.)
Рассмотрение POSIX сигналов выходит за рамки нашего курса. Желающие могут самостоятельно просмотреть описания функций и системных вызовов sigemptyset(), sigfillset(), sigaddset(), sigdelset(), sigismember(), sigaction(), sigprocmask(), sigpending(), sigsuspend() в UNIX Manual.
Задача повышенной сложности: модифицируйте обработку сигнала в программе из этого раздела, не применяя POSIX-сигналы, так, чтобы процесс-родитель все-таки сообщал о статусе всех завершившихся процессов-детей.