Жизненный цикл: fork, exec, exit
Как рождается процесс: fork, exec, exit, дерево процессов.
Как рождается процесс
Процесс не собирается с нуля. Ядро создаёт новый процесс, копируя уже
существующий. Системный вызов fork делает дубликат вызвавшего процесса:
у копии новый PID, но тот же образ памяти, те же открытые файлы и та же
текущая директория. После fork их два, и оба продолжают выполняться со
следующей строки после вызова.
Отличить родителя от потомка просто: fork возвращает разное значение.
Родителю приходит PID потомка, потомку ноль.
pid = fork(); # родитель и потомок оба тут
# у родителя pid = 4210 (PID потомка)
# у потомка pid = 0
exec заменяет образ
Дубликат родителя редко нужен сам по себе. Обычно потомок хочет запустить
другую программу. Для этого он вызывает exec — семейство системных
вызовов, которое заменяет образ текущего процесса на новую программу.
PID при этом не меняется, меняется содержимое: код, данные, стек.
# внутри потомка после fork
exec("/bin/ls", ...); # процесс перестаёт быть копией shell
# и становится ls, PID тот же
Отсюда модель fork+exec. Shell форкается, а в потомке делает exec
нужной команды. Родительский shell при этом остаётся жив и ждёт, пока
потомок отработает.
$ ls # bash форкается, потомок делает exec("ls"), bash ждёт
file1 file2
$ # потомок завершился, управление вернулось в bash
PID, PPID и дерево
У процесса два номера, которые задают его место в системе. PID — уникальный номер самого процесса. PPID — PID его родителя. По этим связям процессы выстраиваются в дерево: терминал породил shell, shell породил команду.
$ echo $$ # PID текущего shell
3187
$ bash # запускаем вложенный shell
$ echo $$ # у него свой PID
4055
$ echo $PPID # а его родитель — прежний shell
3187
Дерево целиком показывает pstree. В корне сидит PID 1 (обычно
systemd), от него ветвятся все остальные.
$ pstree -p
systemd(1)---sshd(880)---sshd(3140)---bash(3187)---pstree(4102)
exit и код возврата
Процесс завершается вызовом exit с целым числом, которое называют
кодом возврата. Ноль означает успех, любое ненулевое значение указывает
на ошибку. Родитель читает
этот код и решает, что делать дальше.
В shell код последней команды лежит в переменной $?.
$ ls /etc/hostname
/etc/hostname
$ echo $? # успех
0
$ ls /нет-такого
ls: cannot access '/нет-такого': No such file or directory
$ echo $? # ошибка
2
Сигналы завершения и осиротевшие процессы
Завершить процесс можно и сигналом. Два сигнала процесс не в силах ни
перехватить, ни заблокировать, ни проигнорировать: SIGKILL и SIGSTOP.
Это единственная такая пара, для всех остальных обработчик поставить
можно.
У каждого сигнала есть действие по умолчанию, всего их пять: завершить
процесс (Term), проигнорировать (Ign), завершить со снимком памяти для
отладки (Core), остановить (Stop), продолжить остановленный (Cont).
SIGCHLD, которым ядро сообщает родителю о смерти потомка, по умолчанию
попадает в Ign.
Когда потомок завершился, а родитель ещё не прочитал его код через
wait(), остаётся зомби. Он держит запись в таблице процессов,
то есть занятый номер PID, но не память. Как только родитель делает
wait(), запись убирается.
$ ps aux | grep defunct
deploy 4310 0.0 0.0 0 0 Z 10:20 0:00 [worker] <defunct> # зомби
Если родитель умирает раньше потомка, потомок становится осиротевшим.
Такого сироту усыновляет процесс с PID 1 (обычно init/systemd), и
он же вызывает wait(), забирая код возврата. С ядра 3.4 роль
усыновителя можно назначить и обычному процессу через subreaper, тогда
сироты уходят к нему, а не к PID 1.
Ловушки
- «Процесс создаётся с нуля» — нет,
forkкопирует родителя со всем его окружением. - «
execпорождает новый процесс» — нет,execзаменяет образ текущего процесса, PID остаётся прежним.
Что важно запомнить
fork делает копию родителя: новый PID, тот же образ памяти и файлы.
exec заменяет образ на другую программу, PID не меняется. Отсюда
модель fork+exec: shell форкается, потомок делает exec.
PID — номер процесса, PPID — номер родителя. pstree рисует дерево от
PID 1.
exit отдаёт код возврата: 0 — успех, ненулевой — ошибка. Смотри $?.
SIGKILL и SIGSTOP нельзя перехватить или проигнорировать. Зомби
держит номер PID, сироту усыновляет PID 1.
В следующем уроке — состояния процесса и планировщик: что значат R, S,
D и как ядро раздаёт им процессор.
Дрилл · вопросы как на собесе
Проверь себя без подсказок
Сформулируй ответ вслух или письменно, потом раскрой и честно отметь: знал или нет.
В чём разница между fork и exec? Что копируется, а что заменяется?
LIN-132Что такое SIGCHLD? В какой ситуации процесс может его получить?
LIN-044Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс