В некоторых реализациях UNIX (например, в HP-UX 11) вызов getsid() можно использовать для получения идентификатора SID только того процесса, который находится в одной сессии с вызывающим (данная возможность оговорена в стандарте SUSv3). Иными словами, исходя из успешного или неудачного (ошибка EPERM) выполнения этого вызова, можно понять, принадлежат ли заданный и вызывающий процессы к одной и той же сессии. Это ограничение не действует в Linux и большинстве других систем.
Если вызывающий процесс не является лидером своей группы, вызов setsid() создает новую сессию.
#include
pid_t setsid(void);
Возвращает идентификатор новой сессии или –1 (pid_t), если случилась ошибка
Создание новой сессии системным вызовом setsid() происходит следующим образом.
• Вызывающий процесс становится лидером новой сессии и новой группы процессов внутри нее. Идентификаторы PGID и SID нового процесса получают то же значение, что и сам процесс.
• Вызывающий процесс не имеет контролирующего терминала. Любое соединение с контролирующим терминалом, установленное ранее, разрывается.
Если вызывающий процесс является лидером своей группы, вызов setsid() завершается ошибкой EPERM. Чтобы этого избежать, проще всего выполнить fork() и дать родителю завершиться, после чего вызвать setsid() из потомка. Поскольку дочерний процесс наследует идентификатор PGID родителя и получает свой собственный идентификатор, он не может оказаться лидером группы.
Ограничение относительно возможности лидера группы вызывать setsid() является необходимым, иначе лидер смог бы переместить себя в другую (новую) сессию, оставляя остальные процессы в исходной сессии (при этом не была бы создана новая группа процессов, поскольку PGID лидера группы по определению совпадает с его собственным идентификатором). Это бы нарушило строгую двухуровневую иерархию сессий и групп процессов, так как все члены группы должны быть частью одной и той же сессии.
При создании нового процесса с помощью вызова fork() ядро выдает ему уникальный идентификатор и проверяет, чтобы этот идентификатор не совпадал с PGID или SID любого существующего процесса. То есть, даже если лидер группы или сессии уже завершился, новый процесс не может повторно использовать его идентификатор, чтобы случайно не стать лидером имеющейся сессии или группы.
В листинге 34.2 демонстрируется использование вызова setsid() для создания новой сессии. Чтобы убедиться в том, что данная программа больше не имеет контролирующего терминала, мы пытаемся открыть специальный файл /dev/tty (описанный в следующем разделе). Запустив программу, мы увидим следующее:
$ ps — p $$ — o 'pid pgid sid command'
PID PGID SID COMMAND
12243 12243 12243 bash
$ ./t_setsid
$ PID=12352, PGID=12352, SID=12352
ERROR [ENXIO Device not configured] open /dev/tty
Как можно видеть по результату, процесс успешно помещает себя в новой группе в рамках новой сессии. Поскольку эта сессия не имеет контролирующего терминала, вызов open() завершается неудачей (в предпоследней строке мы видим, что приглашение командной строки смешивается с программным выводом, потому что командная оболочка узнает о том, что родительский процесс завершился после вызова fork(), и выводит свое следующее приглашение до завершения потомка).
Листинг 34.2. Создание новой сессии
pgsjc/t_setsid.c
#define _XOPEN_SOURCE 500
#include
#include
#include "tlpi_hdr.h"
int
main(int argc, char *argv[])
{
if (fork()!= 0) /* Выходим в случае ошибки или если это родитель */
_exit(EXIT_SUCCESS);
if (setsid() == –1)
errExit("setsid");
printf("PID=%ld, PGID=%ld, SID=%ld\n", (long) getpid(),
(long) getpgrp(), (long) getsid(0));
if (open("/dev/tty", O_RDWR) == –1)
errExit("open /dev/tty");
exit(EXIT_SUCCESS);
}
pgsjc/t_setsid.c
Все процессы в сессии могут иметь (единый) контролирующий терминал, который отсутствует на момент создания сессии, но устанавливается, когда ее лидер впервые открывает терминал, который не является для нее контролирующим (если только при вызове open() не был указан флаг O_NOCTTY). Терминал может быть контролирующим максимум для одной сессии.
Стандарт SUSv3 содержит функцию tcgetsid(int fd) (объявленную в заголовочном файле
Контролирующий терминал наследуется потомком, созданным с помощью fork(), и сохраняется на протяжении работы вызова exec().