Группы процессов, сеансы и терминал

⏱ 30 мин · 🧩 заданий: 5

Зачем группы и сеансы

Shell запускает конвейер cat file | grep x | sort — три процесса, которые должны вместе получать Ctrl+C, вместе останавливаться по Ctrl+Z и вместе уходить в фон. Для этого процессы объединяются в группы, а группы — в сеансы.

СЕАНС SID = 1000 (лидер сеанса: bash, PID 1000) управляющий терминал /dev/pts/0 ┌──────────────────────────────────────────────────────────────────────┐ │ группа 1000: bash │ │ группа 1200 (ФОНОВАЯ): make ─ gcc ← запущено с & │ │ группа 1300 (АКТИВНАЯ): cat ─ grep ─ sort ← получает ввод и Ctrl+C│ └──────────────────────────────────────────────────────────────────────┘

Группа процессов

  • У каждого процесса есть 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 использует это при управлении процессами:

  • при смене уровня выполнения или остановке записи из inittab init посылает сигнал не одному процессу, а всей его группе: 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

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Расставить по порядку

Упорядочьте понятия от самого крупного к самому мелкому:

  1. Группа процессов (process group)
  2. Сеанс (session)
  3. Поток
  4. Процесс
Задание 2 Один вариант ответа

Что произойдёт с фоновым процессом (cmd &), который попытается прочитать с терминала?

Задание 3 Написать код

Сделайте процесс демоном-лидером нового сеанса: создайте потомка через fork, родитель завершается _exit(0), а потомок вызывает setsid() и проверяет результат на -1.

Задание 4 Заполнить таблицу

Кто и почему посылает эти сигналы?

Драйвер терминала (ядро)Ядро при завершении лидера сеанса / закрытии терминалаОбычно программист / пользователь
SIGINT (Ctrl+C)
SIGTSTP (Ctrl+Z)
SIGTTIN (чтение из фона)
SIGHUP (обрыв связи)
SIGCONT (команда fg / bg)
Отметьте ячейку — «+», пустая — «−».
Задание 5 Несколько вариантов

Что верно для лидера сеанса?

Войдите, чтобы отмечать пройденные уроки.