Рисунок 7.6. Диаграмма переходов процесса из состояние в состояние с указанием моментов проверки и обработки сигналов
алгоритм issig /* проверка получения сигналов */
входная информация: отсутствует
выходная информация:
«истина», если процесс получил сигналы, которые его интересуют
«ложь» — в противном случае
{
do while (поле в записи таблицы процессов, содержащее индикацию о получении сигнала, хранит ненулевое значение) {
найти номер сигнала, посланного процессу;
if (сигнал типа «гибель потомка») {
if (сигналы данного типа игнорируются)
освободить записи таблицы процессов, которые соответствуют потомкам, прекратившим существование;
else if (сигналы данного типа принимаются) return (true);
}
else if (сигнал не игнорируется) return (true);
сбросить (погасить) сигнальный разряд, установленный в соответствующем поле таблицы процессов, хранящем индикацию получения сигнала;
}
return (false);
}
Рисунок 7.7. Алгоритм опознания сигналов
7.2.1 Обработка сигналов
Ядро обрабатывает сигналы в контексте того процесса, который получает их, поэтому чтобы обработать сигналы, нужно запустить процесс. Существует три способа обработки сигналов: процесс завершается по получении сигнала, не обращает внимание на сигнал или выполняет особую (пользовательскую) функцию по его получении. Реакцией по умолчанию со стороны процесса, исполняемого в режиме ядра, является вызов функции exit, однако с помощью функции signal процесс может указать другие специальные действия, принимаемые по получении тех или иных сигналов.
Синтаксис вызова системной функции signaclass="underline"
oldfunction = signal(signum, function);
где signum — номер сигнала, при получении которого будет выполнено действие, связанное с запуском пользовательской функции, function — адрес функции, oldfunction — возвращаемое функцией значение. Вместо адреса функции процесс может передавать вызываемой процедуре signal числа 1 и 0: если function = 1, процесс будет игнорировать все последующие поступления сигнала с номером signum (особый случай, связанный с игнорированием сигнала «гибель потомка», рассматривается в разделе 7.4), если = 0 (значение по умолчанию), процесс по получении сигнала в режиме ядра завершается. В пространстве процесса поддерживается массив полей для обработки сигналов, по одному полю на каждый определенный в системе сигнал. В поле, соответствующем сигналу с указанным номером, ядро сохраняет адрес пользовательской функции, вызываемой по получении сигнала процессом. Способ обработки сигналов одного типа не влияет на обработку сигналов других типов.
алгоритм psig /* обработка сигналов после проверки их существования */
входная информация: отсутствует
выходная информация: отсутствует
{
выбрать номер сигнала из записи таблицы процессов;
очистить поле с номером сигнала;
if (пользователь ранее вызывал функцию signal, с помощью которой сделал указание игнорировать сигнал данного типа)
return;
if (пользователь указал функцию, которую нужно выполнить по получении сигнала) {
из пространства процесса выбрать пользовательский виртуальный адрес функции обработки сигнала;
/* следующий оператор имеет нежелательные побочные эффекты */
очистить поле в пространстве процесса, содержащее адрес функции обработки сигнала;
внести изменения в пользовательский контекст:
искусственно создать в стеке задачи запись, имитирующую обращение к функции обработки сигнала;
внести изменения в системный контекст:
записать адрес функции обработки сигнала в поле счетчика команд, принадлежащее сохраненному регистровому контексту задачи;
return;
}
if (сигнал требует дампирования образа процесса в памяти) {
создать в текущем каталоге файл с именем «core»;
переписать в файл «core» содержимое пользовательского контекста;
}
немедленно запустить алгоритм exit;
}
Рисунок 7.8. Алгоритм обработки сигналов
Обрабатывая сигнал (Рисунок 7.8), ядро определяет тип сигнала и очищает (гасит) разряд в записи таблицы процессов, соответствующий данному типу сигнала и установленный в момент получения сигнала процессом. Если функции обработки сигнала присвоено значение по умолчанию, ядро в отдельных случаях перед завершением процесса сбрасывает на внешний носитель (дампирует) образ процесса в памяти (см. упражнение 7.7). Дампирование удобно для программистов тем, что позволяет установить причину завершения процесса и посредством этого вести отладку программ. Ядро дампирует состояние памяти при поступлении сигналов, которые сообщают о каких-нибудь ошибках в выполнении процессов, как например, попытка исполнения запрещенной команды или обращение к адресу, находящемуся за пределами виртуального адресного пространства процесса. Ядро не дампирует состояние памяти, если сигнал не связан с программной ошибкой. Например, прерывание, вызванное нажатием клавиш «delete» или «break» на терминале, имеет своим результатом посылку сигнала, который сообщает о том, что пользователь хочет раньше времени завершить процесс, в то время как сигнал о «зависании» является свидетельством нарушения связи с регистрационным терминалом. Эти сигналы не связаны с ошибками в протекании процесса. Сигнал о выходе (quit), однако, вызывает сброс состояния памяти, несмотря на то, что он возникает за пределами выполняемого процесса. Этот сигнал, обычно вызываемый одновременным нажатием клавиш ‹Ctrl/|›, дает программисту возможность получать дамп состояния памяти в любой момент после запуска процесса, что бывает необходимо, если процесс попадает в бесконечный цикл выполнения одних и тех же команд (зацикливается).