В разделе 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
• Если программа является начальным процессом конвейера, она отображает заголовки для вывода, сгенерированного всеми его участниками
• Программа формирует сообщение, которое будет передано следующему звену конвейера. Этим сообщением является целое число, обозначающее местоположение процесса в конвейере. Таким образом, исходное сообщение инициализируется значением 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)
{
/* НЕБЕЗОПАСНО: этот обработчик использует функции, не рассчитанные на безопасную