Архитектура операционной системы UNIX
Шрифт:
• Сигналы, посылаемые в случае возникновения вызываемых процессом особых ситуаций, таких как обращение к адресу, находящемуся за пределами виртуального адресного пространства процесса, или попытка записи в область памяти, открытую только для чтения (например, текст программы), или попытка исполнения привилегированной команды, а также различные аппаратные ошибки;
• Сигналы, посылаемые во время выполнения системной функции при возникновении неисправимых ошибок, таких как исчерпание системных ресурсов во время выполнения функции exec после освобождения исходного адресного пространства (см. раздел 7.5);
• Сигналы, причиной которых служит возникновение во время выполнения системной функции совершенно неожиданных ошибок, таких как
21
Использование сигналов в некоторых обстоятельствах позволяет обнаружить ошибки при выполнении программ, не проверяющих код завершения вызываемых системных функций (сообщил Д.Ричи).
• Сигналы, посылаемые процессу, который выполняется в режиме задачи, например, сигнал тревоги (alarm), посылаемый по истечении определенного периода времени, или произвольные сигналы, которыми обмениваются процессы, использующие функцию kill;
• Сигналы, связанные с терминальным взаимодействием, например, с «зависанием» терминала (когда сигнал-носитель на терминальной линии прекращается по любой причине) или с нажатием клавиш «break» и «delete» на клавиатуре терминала;
• Сигналы, с помощью которых производится трассировка выполнения процесса. Условия применения сигналов каждой группы будут рассмотрены в этой и последующих главах.
Концепция сигналов имеет несколько аспектов, связанных с тем, каким образом ядро посылает сигнал процессу, каким образом процесс обрабатывает сигнал и управляет реакцией на него. Посылая сигнал процессу, ядро устанавливает в единицу разряд в поле сигнала записи таблицы процессов, соответствующий типу сигнала. Если процесс находится в состоянии приостанова с приоритетом, допускающим прерывания, ядро возобновит его выполнение. На этом роль отправителя сигнала (процесса или ядра) исчерпывается. Процесс может запоминать сигналы различных типов, но не имеет возможности запоминать количество получаемых сигналов каждого типа. Например, если процесс получает сигнал о «зависании» или об удалении процесса из системы, он устанавливает в единицу соответствующие разряды в поле сигналов таблицы процессов, но не может сказать, сколько экземпляров сигнала каждого типа он получил.
Ядро проверяет получение сигнала, когда процесс собирается перейти из режима ядра в режим задачи, а также когда он переходит в состояние приостанова или выходит из этого состояния с достаточно низким приоритетом планирования (см.Рисунок 7.6). Ядро обрабатывает сигналы только тогда, когда процесс возвращается из режима ядра в режим задачи. Таким образом, сигнал не оказывает немедленного воздействия на поведение процесса, исполняемого в режиме ядра. Если процесс исполняется в режиме задачи, а ядро тем временем обрабатывает прерывание, послужившее поводом для посылки процессу сигнала, ядро распознает и обработает сигнал по выходе из прерывания. Таким образом, процесс не будет исполняться в режиме задачи, пока какие-то сигналы остаются необработанными.
На Рисунке 7.7 представлен алгоритм, с помощью которого ядро определяет, получил ли процесс сигнал или нет. Условия, в которых формируются сигналы типа «гибель потомка», будут рассмотрены позже. Мы также увидим, что процесс может игнорировать отдельные сигналы, если воспользуется
Рисунок 7.6. Диаграмма переходов процесса из состояние в состояние с указанием моментов проверки и обработки сигналов
Рисунок 7.7. Алгоритм опознания сигналов
7.2.1 Обработка сигналов
Ядро обрабатывает сигналы в контексте того процесса, который получает их, поэтому чтобы обработать сигналы, нужно запустить процесс. Существует три способа обработки сигналов: процесс завершается по получении сигнала, не обращает внимание на сигнал или выполняет особую (пользовательскую) функцию по его получении. Реакцией по умолчанию со стороны процесса, исполняемого в режиме ядра, является вызов функции exit, однако с помощью функции signal процесс может указать другие специальные действия, принимаемые по получении тех или иных сигналов.
Синтаксис вызова системной функции signal:
oldfunction = signal(signum, function);
где signum — номер сигнала, при получении которого будет выполнено действие, связанное с запуском пользовательской функции, function — адрес функции, oldfunction — возвращаемое функцией значение. Вместо адреса функции процесс может передавать вызываемой процедуре signal числа 1 и 0: если function = 1, процесс будет игнорировать все последующие поступления сигнала с номером signum (особый случай, связанный с игнорированием сигнала «гибель потомка», рассматривается в разделе 7.4), если = 0 (значение по умолчанию), процесс по получении сигнала в режиме ядра завершается. В пространстве процесса поддерживается массив полей для обработки сигналов, по одному полю на каждый определенный в системе сигнал. В поле, соответствующем сигналу с указанным номером, ядро сохраняет адрес пользовательской функции, вызываемой по получении сигнала процессом. Способ обработки сигналов одного типа не влияет на обработку сигналов других типов.