ОП.05Процессы: PID, состояния и сигналызанятие 7Материалы

Часть 2 из 7

Родители и потомки: PPID и дерево процессов

В этой части разберём, как один процесс создаёт другой, что такое PPID, как выглядит дерево процессов и кто становится родителем процесса, если его родитель завершился.

  1. 2.1 Как появляется новый процесс
  2. 2.2 Дерево процессов
  3. 2.3 Сироты

2.1 Как появляется новый процесс

Новый процесс в Linux создаёт только другой процесс. Когда вы вводите в терминале sleep 5, оболочка bash делает два шага. Сначала она создаёт свою копию — системный вызов fork: появляется второй процесс bash с новым PID и той же памятью. Затем копия заменяет свою программу на sleep — вызов exec: PID остаётся прежним, а код и данные загружаются из /usr/bin/sleep. Исходная оболочка в это время ждёт завершения потомка, а затем снова выводит приглашение.

fork и exec похожи на работу с бланком: сначала делают копию бланка со всеми заполненными полями, а потом на копии стирают содержимое и вписывают новое. Номер у копии свой, а исходный бланк остаётся нетронутым.

Поэтому процесс наследует от родителя окружение: текущий каталог, переменные окружения, маску umask, открытые файлы. Всё это было в памяти копии до вызова exec и после него сохранилось. Поэтому /proc/PID/status на прошлом снимке показал маску 0002 — ту же, что у оболочки.

На схеме ниже по шагам показан запуск команды из оболочки: создание копии, замена программы, ожидание и код завершения, который получает родитель.

Схема · как оболочка запускает команду

    2.2 Дерево процессов

    Каждый процесс создан другим, поэтому все процессы системы образуют дерево. В корне — процесс с PID 1, который ядро запускает первым при загрузке. В Ubuntu это systemd: на снимке он виден как /sbin/init, это ссылка на systemd. Его PPID равен 0 — у первого процесса родителя нет.

    Переменная $$ хранит PID текущей оболочки. Команда ps без выбора процессов показывает процессы текущего терминала, ключ -o задаёт столбцы. Команда pstree рисует дерево; ключ -p добавляет PID к каждому имени, а аргумент — с какого процесса начинать.

    В опыте bash -c 'sleep 200; echo done' запускает отдельную оболочку, которая выполнит две команды по очереди. Она сама становится родителем sleep, поэтому в дереве появляется цепочка из трёх уровней: оболочка терминала → вложенная bash → sleep.

    В терминалеРодители и дерево
    1. echo $$# PID оболочки терминала
      что делает

      Что делает команда. Печатает PID текущей оболочки.

      По частям:

      echo
      напечатать текст или значение переменной
      $$
      что напечатать, PID текущей оболочки
    2. bash -c 'sleep 200; echo done' &# вложенная оболочка в фоне
      что делает

      Что делает команда. Запускает в фоне отдельную оболочку, которая выполнит sleep 200, а потом echo; она становится родителем sleep.

      По частям:

      bash -c '…'
      запустить отдельную оболочку bash и выполнить команды из строки
      sleep 200; echo done
      две команды по очереди: оболочка остаётся родителем sleep
      &
      запустить в фоне
    3. ps -o pid,ppid,stat,cmd# процессы терминала с родителями
      что делает

      Что делает команда. Выводит процессы текущего терминала: номер, родителя, состояние и команду.

      По частям:

      ps
      вывести сведения о процессах
      -o pid,ppid,stat,cmd
      выводить столбцы pid,ppid,stat,cmd
    4. pstree -p $$# дерево от оболочки
      что делает

      Что делает команда. Рисует дерево процессов от текущей оболочки с номерами.

      По частям:

      pstree
      нарисовать дерево процессов
      -p
      показывать PID рядом с именем
      $$
      с какого процесса начинать, PID текущей оболочки
    5. ps -p 1 -o pid,ppid,user,cmd# корень дерева
      что делает

      Что делает команда. Выводит сведения о процессе с PID 1 — корне дерева.

      По частям:

      ps
      вывести сведения о процессах
      -p 1
      выбрать процесс с PID 1
      -o pid,ppid,user,cmd
      выводить столбцы pid,ppid,user,cmd
    Результат

    у процессов терминала PPID — PID оболочки, у sleep 200 PPID — вложенная bash; PID 1 принадлежит root, его PPID равен 0.

    Снимок 2.1Ubuntu 24.04
    1. 1PID оболочки терминала
    2. 2оболочка: свой PID и PPID — окно терминала
    3. 3вложенная bash 4230 — родитель sleep 200
    4. 4pstree: цепочка оболочка → bash → sleep
    5. 5PID 1: первый процесс системы, родителя нет
    Процессы терминала, их родители и дерево от оболочки.Весь снимок ↗

    Процессы терминала, их родители и дерево от оболочки.

    Что показывает снимок. У процессов терминала родитель — оболочка 3537; у sleep 200 родитель — вложенная bash 4230; pstree рисует цепочку из трёх уровней; PID 1 — /sbin/init, родителя у него нет.

    Где это пригодится. Дерево показывает, кто запустил подозрительный процесс: по цепочке родителей видно, пришёл он из службы, из терминала или из другой программы.

    2.3 Сироты

    Если родитель завершается раньше потомка, потомок не исчезает. Его «усыновляет» другой процесс — PID 1 или служебный процесс systemd --user, который ведёт сеанс пользователя. PPID потомка меняется на номер нового родителя.

    Сироты встречаются при завершении оболочки, из которой запускали фоновые задачи. Например, если в опыте выше завершить вложенную bash, sleep 200 продолжит работу, а в столбце PPID у него появится номер процесса-приёмника.

    Итоги части 2

    • Новый процесс создаёт другой процесс: fork делает копию, exec заменяет программу копии.
    • PPID — номер родителя; потомок наследует от родителя каталог, переменные окружения и маску.
    • $$ — PID текущей оболочки; pstree -p рисует дерево процессов с номерами.
    • Корень дерева — PID 1, systemd; сирот усыновляет PID 1 или процесс-приёмник сеанса.

    В отчётСнимок pstree с подписанной цепочкой оболочка → bash → sleep.

    Открыть в отдельной вкладке ↗