В разделе 20.5 мы рассматривали сигналы, которые можно отправлять процессу, только если настоящий или действующий пользовательский идентификатор отправителя совпадает с действующим или установленным пользовательским идентификатором получателя. Однако сигнал SIGCONT является исключением из этого правила. Ядро позволяет программе (например, командной оболочке) отправлять сигнал SIGCONT любому процессу в той же сессии, независимо от учетных данных этого процесса. Это послабление требуется для того, чтобы мы по-прежнему могли возобновить работу остановленной программы с помощью сигнала SIGCONT, даже если она изменила свои учетные данные (в частности, настоящий идентификатор пользователя).

Сигналы SIGTTIN и SIGTTOU

Стандарт SUSv3 предусматривает (а Linux реализует) некоторые специальные сценарии, касающиеся отправки фоновым заданиям сигналов SIGTTIN и SIGTTOU.

• Сигнал SIGTTIN не отправляется, если в этот момент процесс его блокирует или игнорирует. Вместо этого вызов read(), выполненный из контролирующего терминала, завершается неудачей и присваивает аргументу errno значение EIO. Это объясняется тем, что в противном случае процесс не смог бы узнать, что операция read() не была разрешена.

• Даже если для терминала установлен флаг TOSTOP, сигнал SIGTTOU не отправляется, если в этот момент процесс его блокирует или игнорирует. Вместо этого разрешается выполнение записи в контролирующий терминал (то есть флаг TOSTOP игнорируется).

• Вне зависимости от наличия флага TOSTOP определенные функции, изменяющие структуры данных терминального драйвера, приводят к отправке фоновому процессу сигнала SIGTTOU, если тот пытается применить их к своему контролирующему терминалу. Это относится к функциям tcsetpgrp(), tcsetattr(), tcflush(), tcflow(), tcsendbreak() и tcdrain() (они будут описаны в главе 58). Их выполнение проходит успешно, если SIGTTOU блокируется или игнорируется.

Пример программы: демонстрация управления заданиями

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

$ ./job_mon |./job_mon |./job_mon

Программа из листинга 34.5 выполняет следующие шаги.

• При запуске программа устанавливает единый обработчик для сигналов SIGINT, SIGTSTP и SIGCONT . Этот обработчик выполняет такие действия.

• Отображает активную группу процессов терминала . Чтобы избежать вывода нескольких одинаковых строк, это делается только лидером группы.

• Отображает идентификатор процесса, его место в конвейере и полученный сигнал .

• В случае перехвата SIGTSTP обработчик должен проделать дополнительную работу, поскольку этот сигнал не останавливает процесс. В связи с этим обработчик генерирует SIGSTOP , который всегда приводит к остановке процесса (в подразделе 34.7.3 будет предложен улучшенный способ обработки SIGTSTP).

• Если программа является начальным процессом конвейера, она отображает заголовки для вывода, сгенерированного всеми его участниками . Чтобы узнать, является ли процесс начальным (или завершающим), мы используем функцию isatty() (описанную в разделе 58.10), которая проверяет, связан ли стандартный ввод (или вывод) с терминалом . Если заданный файл указывает на элемент конвейера, isatty() возвращает отрицательный ответ (0).

• Программа формирует сообщение, которое будет передано следующему звену конвейера. Этим сообщением является целое число, обозначающее местоположение процесса в конвейере. Таким образом, исходное сообщение инициализируется значением 0; дальше начальный процесс передает сообщение 1. Если процесс не является первым в цепочке, он считывает сообщение от своего предшественника и инкрементирует его, прежде чем выполнять последующие шаги .

• Вне зависимости от своего положения в конвейере программа выводит строку со своим порядковым номером, идентификаторами своего и родительского процессов, а также идентификаторами группы и сессии .

• Если программа не является последней в цепочке, она записывает сообщение с числом для следующего процесса .

• В конце программа входит в бесконечный цикл и использует вызов pause() в ожидании сигналов .

Листинг 34.5. Как управление заданиями отражается на работе процессов

pgsjc/job_mon.c

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

#include

#include

#include

#include "tlpi_hdr.h"

static int cmdNum; /* Наше положение в конвейере */

static void /* Обработчик для различных сигналов */

handler(int sig)

{

/* НЕБЕЗОПАСНО: этот обработчик использует функции, не рассчитанные на безопасную

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

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