Группы процессов, сеансы и терминал
Зачем группы и сеансы
Shell запускает конвейер cat file | grep x | sort — три процесса, которые должны вместе получать Ctrl+C, вместе останавливаться по Ctrl+Z и вместе уходить в фон. Для этого процессы объединяются в группы, а группы — в сеансы.
Группа процессов
- У каждого процесса есть PGID — идентификатор группы. Лидер группы — процесс, чей PID равен PGID.
- Потомок после
forkнаследует группу родителя. setpgid(pid, pgid)— перевести процесс в группу (shell создаёт для каждого задания новую группу).- Сигнал можно послать всей группе:
kill(-pgid, sig)илиkill(0, sig)— своей группе.
kill(0, SIGINT) в задаче 8
При ошибке fork в цикле стоит kill(0, SIGINT); — сигнал всей группе процессов вызывающего. Все процессы программы (корневой, C0…Ck) находятся в одной группе, поэтому все получат SIGINT и по умолчанию завершатся — в том числе корневой процесс, который ждёт в wait.
Сеанс
- Сеанс — набор групп процессов; обычно соответствует одному входу пользователя в систему.
- Лидер сеанса — процесс, создавший сеанс вызовом
setsid(); его PID = SID. - У сеанса может быть один управляющий терминал. Среди групп сеанса одна — активная (foreground): она получает ввод с терминала и сигналы от клавиатуры. Остальные — фоновые.
pid_t sid = getsid(0); /* SID текущего процесса */
pid_t pg = getpgrp(); /* PGID */
setsid(); /* новый сеанс + новая группа, без терминала */
tcsetpgrp(fd, pgid); /* сделать группу активной на терминале (так делает fg) */
ps -eo pid,ppid,pgid,sid,tty,stat,comm
Сигналы терминала
| Событие | Сигнал | Кому | Действие по умолчанию |
|---|---|---|---|
| Ctrl+C | SIGINT |
активной группе | завершить |
| Ctrl+\ | SIGQUIT |
активной группе | завершить + core |
| Ctrl+Z | SIGTSTP |
активной группе | остановить |
| фоновый процесс читает терминал | SIGTTIN |
его группе | остановить |
фоновый процесс пишет (при stty tostop) |
SIGTTOU |
его группе | остановить |
| терминал закрыт / лидер сеанса вышел | SIGHUP |
лидеру / активной группе | завершить |
fg, bg |
SIGCONT |
группе задания | продолжить |
Именно поэтому nohup cmd & (игнорирует SIGHUP) или setsid cmd (новый сеанс без терминала) позволяют программе пережить выход из системы.
Демоны
Демон — фоновый процесс без терминала. Классическая «демонизация»:
if (fork() > 0) _exit(0); /* 1. родитель выходит, shell считает команду выполненной */
setsid(); /* 2. новый сеанс: нет управляющего терминала */
if (fork() > 0) _exit(0); /* 3. не лидер сеанса — не сможет случайно получить терминал */
chdir("/"); /* 4. не держать смонтированные ФС */
umask(0);
close(0); close(1); close(2); /* 5. отвязаться от терминала */
Как init учитывает идентификатор сеанса
Вопрос 3(в) экзамена. Ответ складывается из двух частей.
1. init создаёт сеансы. Процессы из /etc/inittab — прежде всего getty на каждом терминале — SysV init запускает так: fork, затем в потомке setsid(), затем exec. Каждый такой процесс — лидер собственного сеанса и группы (PID = SID = PGID). getty открывает терминал — он становится управляющим терминалом этого сеанса; затем getty → login → shell выполняются в том же процессе, сохраняя сеанс.
2. init использует это при управлении процессами:
- при смене уровня выполнения или остановке записи из
inittabinit посылает сигнал не одному процессу, а всей его группе:kill(-pid, SIGTERM), затемkill(-pid, SIGKILL). Так завершается весь сеанс пользователя на терминале, а не толькоgetty; - в базе входов utmp init и
loginведут записи сеанса: PID, терминал, тип (INIT_PROCESS,LOGIN_PROCESS,USER_PROCESS), в структуре есть полеut_session— идентификатор сеанса. Когда лидер сеанса завершается, init получаетSIGCHLD, собирает его черезwait, помечает запись какDEAD_PROCESS(это видятwho,w) и при действииrespawnзапускает новыйgettyв новом сеансе.
Ещё один момент: процессы сеанса, пережившие лидера (например, запущенные через nohup), становятся сиротами — их усыновляет init и собирает их статус при завершении. Сеанс при этом сохраняет SID, хотя лидера уже нет.
Современный systemd
systemd следит за процессами службы и сеанса пользователя через cgroups (и сервис systemd-logind для сеансов входа): процесс не может «сбежать» из cgroup, даже вызвав setsid. Это более надёжная замена идентификатору сеанса.
Итог
- Иерархия: сеанс → группы процессов → процессы → потоки.
- Группа получает сигналы вместе (
kill(-pgid),kill(0, ...)); shell создаёт группу на каждое задание. - Сеанс имеет лидера и управляющий терминал; активная группа получает ввод и сигналы клавиатуры, фоновые —
SIGTTIN/SIGTTOU. - init запускает процессы в новых сеансах через
setsidи завершает их сигналами всей группе; учитывает сеансы в utmp.
Практика решено 0 из 5
Упорядочьте понятия от самого крупного к самому мелкому:
- Группа процессов (process group)
- Сеанс (session)
- Поток
- Процесс
Что произойдёт с фоновым процессом (cmd &), который попытается прочитать с терминала?
Сделайте процесс демоном-лидером нового сеанса: создайте потомка через fork, родитель завершается _exit(0),
а потомок вызывает setsid() и проверяет результат на -1.
pid_t pid = fork();
if (pid > 0)
_exit(0);
if (setsid() == -1) {
perror("setsid");
_exit(1);
}
Кто и почему посылает эти сигналы?
| Драйвер терминала (ядро) | Ядро при завершении лидера сеанса / закрытии терминала | Обычно программист / пользователь | |
|---|---|---|---|
| SIGINT (Ctrl+C) | |||
| SIGTSTP (Ctrl+Z) | |||
| SIGTTIN (чтение из фона) | |||
| SIGHUP (обрыв связи) | |||
| SIGCONT (команда fg / bg) |
Что верно для лидера сеанса?
Войдите, чтобы отмечать пройденные уроки.