Московский физико-технический институт
Опубликован: 16.09.2004 | Доступ: свободный | Студентов: 7346 / 1504 | Оценка: 4.44 / 4.28 | Длительность: 15:33:00
Лекция 9:

Организация ввода-вывода в UNIX. Файлы устройств. Аппарат прерываний. Сигналы в UNIX

Изучение особенностей получения терминальных сигналов текущей и фоновой группой процессов

Возьмем тривиальную программу 13–14-1.c, в которой процесс порождает ребенка, и они оба зацикливаются, и на ее основе проиллюстрируем сказанное выше.

/* Тривиальная программа для иллюстрации 
понятий группа процессов, сеанс, фоновая 
группа и т.д. */
#include <unistd.h>
int main(void){ 
    (void)fork();
    while(1);
    return 0;
}
Листинг 13-14.1. Тривиальная программа (13–14-1.c) для иллюстрации понятий группа процессов, сеанс, фоновая группа и т.д.

Для этого будем использовать команду ps с опциями –e и j, которая позволяет получить информацию обо всех процессах в системе и узнать их идентификаторы, идентификаторы групп процессов и сеансов, управляющий терминал сеанса и к какой группе процессов он приписан. Набрав команду "ps –e j" ( обратите внимание на наличие пробела между буквами e и j!!!) мы получим список всех процессов в системе. Колонка PID содержит идентификаторы процессов, колонка PGID – идентификаторы групп, к которым они принадлежат, колонка SID – идентификаторы сеансов, колонка TTY – номер соответствующего управляющего терминала, колонка TPGID (может присутствовать не во всех версиях UNIX, но в Linux есть) – к какой группе процессов приписан управляющий терминал.

Наберите тривиальную программу, откомпилируйте ее и запустите на исполнение (лучше всего из-под оболочки Midnight Commander – mc). Запустив команду "ps –e j" с другого экрана, проанализируйте значения идентификаторов группы процессов, сеансов, прикрепления управляющего терминала, текущей и фоновой групп. Убедитесь, что тривиальные процессы относятся к текущей группе сеанса. Проверьте реакцию текущей группы на сигналы SIGINT – нажатие клавиш <CTRL> и <C> – и SIGQUIT – нажатие клавиш <CTRL> и <4>.

Запустите теперь тривиальную программу в фоновом режиме, например командой "a.out &". Проанализируйте значения идентификаторов группы процессов, сеансов, прикрепления управляющего терминала, текущей и фоновой групп. Убедитесь, что тривиальные процессы относятся к фоновой группе сеанса. Проверьте реакцию фоновой группы на сигналы SIGINT – нажатие клавиш <CTRL> и <C> – и SIGQUIT – нажатие клавиш <CTRL> и <4>. Ликвидируйте тривиальные процессы с помощью команды kill .

Изучение получения сигнала SIGHUP процессами при завершении лидера сеанса

Возьмите снова тривиальную программу из предыдущего раздела и запустите ее на исполнение из-под Midnight Commander в текущей группе. Проанализировав значения идентификаторов группы процессов, сеансов, прикрепления управляющего терминала, текущей и фоновой групп, ликвидируйте лидера сеанса для тривиальных процессов. Убедитесь, что все процессы в текущей группе этого сеанса прекратили свою работу.

Запустите тривиальную программу в фоновом режиме. Снова удалите лидера сеанса для тривиальных процессов. Убедитесь, что фоновая группа продолжает работать. Ликвидируйте тривиальные процессы.

Системный вызов signal(). Установка собственного обработчика сигнала

Одним из способов изменения поведения процесса при получении сигнала в операционной системе UNIX является использование системного вызова signal() .

Системный вызов signal()

Прототип системного вызова

#include <signal.h>
void (*signal (int sig, 
      void (*handler) (int)))(int);

Описание системного вызова

Системный вызов signal служит для изменения реакции процесса на какой-либо сигнал . Хотя прототип системного вызова выглядит довольно пугающе, ничего страшного в нем нет. Приведенное выше описание можно словесно изложить следующим образом:

функция signal, возвращающая указатель на функцию с одним параметром типа int, которая ничего не возвращает, и имеющая два параметра: параметр sig типа int и параметр handler, служащий указателем на ничего не возвращающую функцию с одним параметром типа int.

Параметр sig – это номер сигнала, обработку которого предстоит изменить.

Параметр handler описывает новый способ обработки сигнала – это может быть указатель на пользовательскую функцию – обработчик сигнала, специальное значение SIG_DFL или специальное значение SIG_IGN. Специальное значение SIG_IGN используется для того, чтобы процесс игнорировал поступившие сигналы с номером sig, специальное значение SIG_DFL – для восстановления реакции процесса на этот сигнал по умолчанию.

Возвращаемое значение

Системный вызов возвращает указатель на старый способ обработки сигнала, значение которого можно использовать для восстановления старого способа в случае необходимости.

Этот системный вызов имеет два параметра: один из них задает номер сигнала, реакцию процесса на который требуется изменить, а второй определяет, как именно мы собираемся ее менять. Для первого варианта реакции процесса на сигнал (см. раздел "Понятие сигнала. Способы возникновения сигналов и виды их обработки") – его игнорирования – применяется специальное значение этого параметра SIG_IGN. Например, если требуется игнорировать сигнал SIGINT , начиная с некоторого места работы программы, в этом месте программы мы должны употребить конструкцию

(void) signal(SIGINT, SIG_IGN);

Для второго варианта реакции процесса на сигнал – восстановления его обработки по умолчанию – применяется специальное значение этого параметра SIG_DFL. Для третьего варианта реакции процесса на сигнал в качестве значения параметра подставляется указатель на пользовательскую функцию обработки сигнала, которая должна иметь прототип вида

void *handler(int);

Ниже приведен пример скелета конструкции для пользовательской обработки сигнала SIGHUP .

void *my_handler(int nsig) { 
    <обработка сигнала> 
}
int main() { 
    ...
    (void)signal(SIGHUP, my_handler);
    ... 
}

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

Прогон программы, игнорирующей сигнал SIGINT

Рассмотрим следующую программу – 13–14-2.c:

/* Программа, игнорирующая сигнал SIGINT */
#include <signal.h>
int main(void){ 
    /* Выставляем реакцию процесса на сигнал
       SIGINT на игнорирование */ 
    (void)signal(SIGINT, SIG_IGN);
    /*Начиная с этого места, процесс будет 
      игнорировать возникновение сигнала 
      SIGINT */ 
    while(1);
    return 0;
}
Листинг 13-14.2. Программа (13–14-2.c), игнорирующая сигнал SIGINT.

Эта программа не делает ничего полезного, кроме переустановки реакции на нажатие клавиш <CTRL> и <C> на игнорирование возникающего сигнала и своего бесконечного зацикливания. Наберите, откомпилируйте и запустите эту программу, убедитесь, что на нажатие клавиш <CTRL> и <C> она не реагирует, а реакция на нажатие клавиш <CTRL> и <4> осталась прежней.

Модификация предыдущей программы для игнорирования сигналов SIGINT и SIGQUIT

Модифицируйте программу из предыдущего раздела так, чтобы она перестала реагировать и на нажатие клавиш <CTRL> и <4>. Откомпилируйте и запустите ее, убедитесь в отсутствии ее реакций на внешние раздражители. Снимать программу придется теперь с другого терминала командой kill .