Родитель или поток любого процесса в задании могут использовать вызов setpgid() для изменения PGID дочернего процесса. Но поскольку невозможно предсказать, в каком порядке будут выполняться родитель и потомок после вызова fork() (см. раздел 24.4), мы не можем полагаться на то, что родитель изменит PGID до того, как потомок выполнит exec(); мы также не можем быть уверены в том, что потомок изменит свой PGID до того, как родитель попытается передать ему какой-нибудь сигнал для управления заданием (зависимость от любого из этих сценариев приведет к состоянию гонки). Следовательно, командные оболочки, управляющие заданиями, написаны так, чтобы сразу после вызова fork() родитель и его дочерний процесс изменяли идентификатор потомка PGID с помощью setpgid(). При этом родитель игнорирует любые ошибки типа EACCES, возвращаемые вызовом setpgid(). Иными словами, в любой командной оболочке, управляющей заданиями, можно найти код, похожий на содержимое листинга 34.1.

Листинг 34.1. Как командные оболочки, управляющие заданиями, устанавливают идентификатор PGID для дочернего процесса

pid_t childPid;

pid_t pipelinePgid; /* PGID, к которому будет назначен процесс в конвейере */

/* Другой код */

childPid = fork();

switch (childPid) {

case –1: /* fork() завершается неудачей */

/* Обрабатываем ошибку */

case 0: /* Потомок */

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

/* Обрабатываем ошибку */

/* Потомок продолжает работу, запуская заданную программу */

default: /* Родитель (командная оболочка) */

if (setpgid(childPid, pipelinePgid) == –1 && errno!= EACCES)

/* Обрабатываем ошибку */

/* Родитель продолжает работу, выполняя другие задания */

}

На самом деле все немного сложнее, чем показано на рис. 34.1, поскольку при создании процесса для конвейера родительская командная оболочка записывает идентификатор первого процесса в цепочке команд и использует его в качестве PGID (pipelinePgid) для всех процессов в группе.

Другие (устаревшие) интерфейсы для извлечения и изменения идентификаторов PGID

Разница в суффиксах у системных вызовов getpgrp() и setpgid() заслуживает отдельного объяснения.

Изначально система 4.2BSD предоставляла системный вызов getprgp(pid), который возвращал идентификатор PGID для процесса, заданного с помощью аргумента pid. На практике в качестве этого аргумента всегда указывали вызывающий процесс. По этой причине комитет, стоящий за разработкой стандарта POSIX, решил, что данный вызов является более сложным, чем следует, и заменил его вызовом getpgrp() из System V, который не принимает никаких аргументов и возвращает PGID для текущего процесса.

Для изменения идентификатора PGID система предоставляла вызов setpgrp(pid, pgid), который принципом своей работы напоминал setpgid(). Ключевое различие было в том, что с помощью вызова setpgrp() из состава BSD можно было поменять идентификатор PGID на любое значение (ранее отмечалось, что setpgid() не может переместить процесс в группу, которая находится в другой сессии). Это приводило к некоторым проблемам с безопасностью и обеспечивало гибкость, излишнюю для реализации управления заданиями. В результате комитет, разрабатывающий стандарт POSIX, остановился на более строгой функции, назвав ее setpgid().

Но и это еще не все. Стандарт SUSv3 содержит функцию getpgid(pid) с такой же семантикой, как у старого вызова getpgrp() из состава BSD. В нем же ненавязчиво предлагается альтернатива — версия вызова setpgrp(), берущая свое начало в System V; она не принимает никаких аргументов и практически является эквивалентом вызова setpgid(0, 0).

Системные вызовы setpgid() и getpgrp(), описанные выше, удовлетворяют нужды по реализации управления заданиями в командной оболочке, однако в дополнение к ним Linux, как и большинство других UNIX-систем, предоставляет вызовы getpgid(pid) и setpgrp(void). Из соображений обратной совместимости многие BSD-системы продолжают поддерживать вызов setprgp(pid, pgid) в качестве эквивалента setpgid(pid, pgid).

Если при компиляции программы явно объявить макрос _BSD_SOURCE для проверки доступных возможностей, библиотеке glibc предоставит вместо стандартных вызовов setpgrp() и getpgrp() их версии из состава BSD.

34.3. Сессии

Сессия — это набор групп процессов. Принадлежность процесса к сессии определяется числовым идентификатором SID (session ID). Новый процесс наследует SID своего родителя. Системный вызов getsid() возвращает идентификатор SID процесса, указанного с помощью аргумента pid.

#define _XOPEN_SOURCE 500

#include

pid_t getsid(pid_t pid);

Возвращает SID заданного процесса или –1 (pid_t), если случилась ошибка

Если аргумент pid равен 0, getsid() возвращает идентификатор сессии вызывающего процесса.

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

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