Родитель или поток любого процесса в задании могут использовать вызов 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) для всех процессов в группе.
Разница в суффиксах у системных вызовов 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.
#define _XOPEN_SOURCE 500
#include
pid_t getsid(pid_t
Возвращает SID заданного процесса или –1 (pid_t), если случилась ошибка
Если аргумент pid равен 0, getsid() возвращает идентификатор сессии вызывающего процесса.