Выбрать главу

Рисунок 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/|›, дает программисту возможность получать дамп состояния памяти в любой момент после запуска процесса, что бывает необходимо, если процесс попадает в бесконечный цикл выполнения одних и тех же команд (зацикливается).