Читаем Linux API. Исчерпывающее руководство онлайн бесплатно
На примере программы из листинга 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. Однако этот сигнал не был отправлен потомку, который был в отдельной (фоновой) группе.
Возможность управления заданиями впервые появилась в командной оболочке csh в системе BSD; это случилось в 1980 году. Она позволяет пользователю командной строки выполнять одновременно несколько программ (заданий): одну в активном режиме, а все остальные — в фоновом. Задания можно останавливать, возобновлять и переключать между фоновым и активным режимами, как будет показано в следующих подразделах.
Изначально управление заданиями в стандарте POSIX.1 было опциональным. В дальнейших стандартах, относящихся к UNIX, эта возможность стала обязательной.




