На примере программы из листинга 34.4 можно продемонстрировать, что в результате завершения контролирующего процесса сигнал SIGHUP передается всем участникам активной группы терминала. Эта программа создает по одному дочернему процессу на каждый аргумент командной строки . Если аргумент равен d, потомок помещается в отдельную (другую) группу ; в противном случае он остается в одной группе с родителем (для обозначения этого действия мы используем букву s, хотя для этого подойдет любой символ, кроме d). Затем каждый потомок устанавливает обработчик для SIGHUP . Чтобы обеспечить завершение родителя и потомка, даже если оно не было инициировано извне, в каждом из них предусмотрен вызов alarm(), который устанавливает таймер для доставки сигнала SIGALRM через 60 секунд . В конце все процессы (включая родителя) выводят свои идентификаторы и идентификаторы своей группы , после чего входят в цикл и ждут появления сигнала . Когда сигнал получен, обработчик выводит его номер вместе с идентификатором процесса .

Листинг 34.4. Перехватывание сигнала SIGHUP при отключении от терминала

pgsjc/disc_SIGHUP.c

#define _GNU_SOURCE /* Получаем объявление strsignal() из */

#include

#include

#include "tlpi_hdr.h"

static void /* Обработчик сигнала SIGHUP */

handler(int sig)

{

printf("PID %ld: caught signal %2d (%s)\n", (long) getpid(),

sig, strsignal(sig));

/* НЕБЕЗОПАСНО (см. подраздел 21.1.2) */

}

int

main(int argc, char *argv[])

{

pid_t parentPid, childPid;

int j;

struct sigaction sa;

if (argc < 2 || strcmp(argv[1], "-help") == 0)

usageErr("%s {d|s}… [> sig.log 2>&1]\n", argv[0]);

setbuf(stdout, NULL); /* Отключаем буферизацию стандартного вывода */

parentPid = getpid();

printf("PID of parent process is: %ld\n", (long) parentPid);

printf("Foreground process group ID is: %ld\n", (long) tcgetpgrp(STDIN_FILENO));

 for (j = 1; j < argc; j++) { /* Создаем дочерний процесс */

childPid = fork();

if (childPid == –1)

errExit("fork");

if (childPid == 0) { /* Если это потомок… */

 if (argv[j][0] == 'd') /* 'd' означает перемещение в другую группу */

if (setpgid(0, 0) == –1)

errExit("setpgid");

sigemptyset(&sa.sa_mask);

sa.sa_flags = 0;

sa.sa_handler = handler;

 if (sigaction(SIGHUP, &sa, NULL) == –1)

errExit("sigaction");

break; /* Потомок выходит из цикла */

}

}

/* Все процессы доходят сюда */

alarm(60); /* Гарантируем, что все процессы в итоге завершатся */

printf("PID=%ld PGID=%ld\n", (long) getpid(), (long) getpgrp());

for (;;)

 pause(); /* Ждем сигнала */

}

pgsjc/disc_SIGHUP.c

Представьте, что мы запустили программу из листинга 34.4 в окне терминала с помощью следующей команды:

$ exec./disc_SIGHUP d s s > sig.log 2>&1

Встроенная команда exec заставляет командную оболочку выполнить вызов exec() и заменить себя заданной программой. Поскольку командная оболочка была контролирующим процессом для терминала, наша программа теперь сама становится контролирующим процессом и при закрытии терминала получит сигнал SIGHUP; когда это случится, в файле sig.log можно будет найти следующие строки:

PID of parent process is: 12733

Foreground process group ID is: 12733

PID=12755 PGID=12755 Первый потомок попадает в другую группу процессов

PID=12756 PGID=12733 Остальные потомки попадают в одну группу с родителем

PID=12757 PGID=12733

PID=12733 PGID=12733 Это родительский процесс

PID 12756: caught signal 1 (Hangup)

PID 12757: caught signal 1 (Hangup)

Закрытие окна терминала привело к отправке сигнала SIGHUP контролирующему процессу (родителю), который в ответ на это завершил свою работу. Как видно, оба потомка, находившиеся в одной группе с родителем (то есть в активной группе процессов текущего терминала) тоже получили сигнал SIGHUP. Однако этот сигнал не был отправлен потомку, который был в отдельной (фоновой) группе.

34.7. Управление заданиями

Возможность управления заданиями впервые появилась в командной оболочке csh в системе BSD; это случилось в 1980 году. Она позволяет пользователю командной строки выполнять одновременно несколько программ (заданий): одну в активном режиме, а все остальные — в фоновом. Задания можно останавливать, возобновлять и переключать между фоновым и активным режимами, как будет показано в следующих подразделах.

Изначально управление заданиями в стандарте POSIX.1 было опциональным. В дальнейших стандартах, относящихся к UNIX, эта возможность стала обязательной.

Перейти на страницу:

Похожие книги