Возьмем тривиальную программу 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 .
Возьмите снова тривиальную программу из предыдущего раздела и запустите ее на исполнение из-под Midnight Commander в текущей группе. Проанализировав значения идентификаторов группы процессов, сеансов, прикрепления управляющего терминала, текущей и фоновой групп, ликвидируйте лидера сеанса для тривиальных процессов. Убедитесь, что все процессы в текущей группе этого сеанса прекратили свою работу.
Запустите тривиальную программу в фоновом режиме. Снова удалите лидера сеанса для тривиальных процессов. Убедитесь, что фоновая группа продолжает работать. Ликвидируйте тривиальные процессы.
Одним из способов изменения поведения процесса при получении сигнала в операционной системе UNIX является использование системного вызова signal() .
Этот системный вызов имеет два параметра: один из них задает номер сигнала, реакцию процесса на который требуется изменить, а второй определяет, как именно мы собираемся ее менять. Для первого варианта реакции процесса на сигнал (см. раздел "Понятие сигнала. Способы возникновения сигналов и виды их обработки") – его игнорирования – применяется специальное значение этого параметра 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 ) передается номер возникшего сигнала, так что одна и та же функция может быть использована для обработки нескольких сигналов.
Рассмотрим следующую программу – 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> осталась прежней.
Модифицируйте программу из предыдущего раздела так, чтобы она перестала реагировать и на нажатие клавиш <CTRL> и <4>. Откомпилируйте и запустите ее, убедитесь в отсутствии ее реакций на внешние раздражители. Снимать программу придется теперь с другого терминала командой kill .