Для организации диалоговой работы пользователей в Unix вводится понятие терминальной сессии. С точки зрения пользователя - это процесс работы с текстовым терминалом с момента ввода имени и пароля и до выхода из системы командой logout
(exit
, нажатие ^D в пустой строке). Во время терминальной сессии может быть запущено несколько программ, которые будут параллельно выполнятся в фоновом режиме и между которыми можно переключаться в диалоговом режиме. После завершения терминальной сессии возможно принудительное завершение всех запущенных в ней фоновых процессов.
С точки зрения ядра - терминальная сессия - это группа процессов, имеющих один идентификатор сеанса sid. С идентификатором sid связан драйвер управляющего терминала, доступный всем членам сеанса как файл символьного устройства /dev/tty. Для каждого сеанса существует свой /dev/tty. Управляющий терминал взаимодействует с процессами сеанса с помощью отправки сигналов.
В рамках одного сеанса могут существовать несколько групп процессов. С каждым процессом связан идентификатор группы pgid. Одна из групп в сеансе может быть зарегистрирована в драйвере управляющего терминала как группа фоновых процессов. Процессы могут переходить из группы в группу самостоятельно или переводить из группы в группу другие процессы сеанса. Перейти в группу другого сеанса нельзя, но можно создать свой собственный сеанс из одного процесса со своей группой в этом сеансе. Вернуться в предыдущий сеанс уже не получится.
Группа процессов - инструмент для доставки сигнала нескольким процессам, а также способ арбитража при доступе к терминалу. Идентификатор группы pgid равен pid создавшего её процесса - лидера группы. Процесс может переходить из группы в группу внутри одного сеанса.
#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid); // включить процесс pid в группу pgid.
// pid=0 означает текущий процесс,
// pgid=0 означает pgid=pid текущего процесса
// pid=pgid=0 - создание новой группы с pgid=pid текущего процесса
// и переход в эту группу
pid_t getpgid(pid_t pid); // получить номер группы процесса pid.
// pid=0 - текущий процесс
int setpgrp(void); // создание группы, эквивалент setpgid(0.0);
pid_t getpgrp(void); // запрос текущей группы, эквивалент getpgid(0);
Сеанс - средство для контроля путем посылки сигналов над несколькими группами процессов со стороны терминального драйвера. Как правило соответствует диалоговой пользовательской сессии. Идентификатор сеанса sid равняется идентификатору pid, создавшего его процесса - лидера сеанса. Одновременно с сеансом создаётся новая группа с pgid равным pid лидера сеанса. Поскольку переход группы из сеанса в сеанс невозможен, то создающий сеанс процесс не может быть лидером группы.
#include <unistd.h>
pid_t setsid(void); //Создание новой группы и нового сеанса. Текущий процесс не должен быть лидером группы.
pid_t getsid(pid_t pid); //Возвращает номер сеанса для указанного процесса
Создание собственного сеанса рекомендуется начать с fork, чтобы гарантировать, что процесс не является лидером группы.
if( fork() ) exit(0);
setsid();
Процессы в фоновой группе выполняются до тех пор, пока не попытаются осуществить чтение или запись через файловый дескриптор управляющего терминала. В этот момент они получают сигнал SIGTTIN или SIGTTOU соответственно. Действие по умолчанию для данного сигнала - приостановка выполнения процесса.
Назначение фоновой группы:
#include <unistd.h>
pid_t tcgetpgrp(int fd); // получить pgid фоновой группы, связанной с управляющим терминалом,
// на который ссылается файловый дескриптор fd
int tcsetpgrp(int fd, pid_t pgrp); // назначить pgid фоновой группы терминалу,
// на который ссылается файловый дескриптор fd
Некоторые сочетания клавиш позволяют посылать сигналы процессам сеанса:
bash
отслеживает остановку дочерних процессов и вносит их в списки своих фоновых процессов. Остановленный процесс может быть продолжен командой fg n
, где n - порядковый нрмер процесса в списке фоновых процессовОткрыть управляющий терминал сеанса
#include <stdio.h>
char name[L_ctermid];
int fd;
ctermid(name); // если name=NULL, то используется внутренний буфер
// ctermid возвращает указатель на буфер
// L_ctermid - библиотечная константа
fd = open(name, O_RDWR, 0);