Обзор
Вы когда-нибудь интересовались, что происходит за кулисами, когда мы запускаем или завершаем процесс? В этом уроке мы узнаем, как Linux генерирует PID для процессов.
Таблица процессов в Linux
Ядро Linux использует структуру данных, называемую таблицей процессов, для различных задач, таких как планирование процессов. Каждый раз, когда мы запускаем процесс, ядро вставляет в таблицу запись со следующей информацией:
PID
Родительский процесс
Переменные окружения
Прошедшее время
Статус — один из D (Непрерываемый), R (Выполняется), S (Спящий), T (Остановлен) или Z (Зомби)
Использование памяти
Мы можем получить эту информацию через файловую систему procfs, смонтированную вкаталоге /proc, с помощью различных инструментов мониторинга ресурсов, таких как top.
Давайте посмотрим на некоторые из этих данных, выполнив команду top:
Mem: 4241112K used, 12106916K free, 360040K shrd, 20K buff, 1772160K cached
CPU: 0.8% usr 0.8% sys 0.0% nic 98.3% idle 0.0% io 0.0% irq 0.0% sirq
Load average: 0.26 0.33 0.35 1/489 21888
PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
1 0 root S 2552 0.0 10 0.0 init
30405 30404 baeldung S 2552 0.0 7 0.0 /bin/sh
219 1 root S 2552 0.0 10 0.0 /bin/getty 38400 tty2
1309 1308 baeldung S 2552 0.0 9 0.0 /bin/sh
21873 640 baeldung S 2552 0.0 5 0.0 [sleep]
21874 21758 baeldung R 2552 0.0 8 0.0 top
Генерация PID
Linux назначает идентификаторы процессов последовательно, начиная с 0 и вплоть до максимального установленного значения.
За процессом ядра idle task, который обеспечивает постоянное наличие готовой к выполнению задачи, зарезервирован PID 0, а за системой init, которая явлется первым процессом, зарезервирован PID 1.
Процесс с PID 0 — это холостая задача, которая является «процессом‑заготовкой». Он запускается, когда другие процессы не могут быть запущены, гарантируя, что процессор остается активным и готовым к выполнению других задач по мере их появления.
Мы можем проверить максималоно допустимое значение PID в системе, заглянув в файл /proc/sys/kernel/pid_max. Обычно это 5-значное число:
$ cat /proc/sys/kernel/pid_max
32768
Мы можем настроить ограничение максимум до 222 (4 194 304), записав под root желаемое число в файл:
# desired=4194304# echo {pid##*/}" # Извлечение PID [ "highest" ] && highest="highest"for _ in (readlink /proc/self)"done
Наша вторая попытка выдала ошибку, так как число превышает 222.
Когда мы запускаем процесс, для него генерируется PID, позволяющий однозначно идентифицировать его. Это делается просто путем увеличения текущего наивысшего PID на 1.
Давайте подтвердим это с помощью простого скрипта:
!/bin/sh -e
# Этот скрипт предполагает, что printf является встроенной функцией shell и, следовательно, не занимает лишних PID.
highest=0
for pid in /proc/[0-9]*; do
pid="${pid##*/}" # Извлекаем PID
[ "$pid" -gt "$highest" ] && highest="$pid" # -gt означет больше, чем ("greater than")
done
printf "Найбольший PID равен %d\n" "$highest"
for _ in $(seq 4); do
printf "Запущен новый процесс с PID %d\n" "$(readlink /proc/self)"
done
Сначала мы вычислили текущий найбольший PID в системе. Затем мы запустили четыре процесса readlink, каждый из которых отобразил присвоенный ему новый PID.
Взглянем на вывод скрипта:
Наибольший PID - 10522
Запущен новый процесс с PID 10524
Запущен новый процесс с PID 10525
Запущен новый процесс с PID 10526
Запущен новый процесс с PID 10527
Мы можем заметить, что между проверкой наивысшего PID и печатью новых PID существует разрыв в 1 PID, поскольку сама команда seq сама запускает еще один дополнительный процесс. Следовательно, внешние процессы влияет на подобные тесты, т.к. сами процессы создают новые PID.
Могут ли закончиться PIDы?
В предыдущем разделе мы обсуждали максимальное значение PID в системе, так что же произойдет, когда мы достигнем этого предела?
Если мы достигаем максимального значения PID, то поиск следующего PID начинается снова с минимального значения и продолжается, пока не найдётся свободный PID, принадлежащий уже завершённому процессу.
Следует отметить, что процесс считается «завершенным» только когда родительский процесс получил его статус завершения. Таким образом, зловредная программа может исчерпать доступные PID в системе.
Заключение
В этой статье мы узнали об идентификаторах процессов в Linux — как они генерируются, как высоко они могут подниматься и что происходит при достижении предела.
Комментарии (5)
saboteur_kiev
14.07.2024 17:34Это делается просто путем увеличения текущего наивысшего PID на 1
Ну уже ж не просто путем увеличения наивысшего, а где-то хранится определенный счетчик, который инкрементируется, пока не достигнет max pid, потом пойдет с начала, плюс будет проверять что данный pid сейчас свободен. Искать наивысший на текущий момент пид по дереву процессов дольше. Плюс не забываем про баги, из-за которых появляются зомби процессы.
что процесс считается "завершенным" только в том случае, если его статус завершения был получен его родителем.
Словно чатгпт писал. Я не понимаю смысл. Система может прибить процесс, у которого есть дочерние процессы. У них временно вообще родителя может не быть, ОС потом присвоит им родителем процесс с PID1. Я не могу точно сказать в какой момент именно происходит возврат exit code родительскому процессу, но вполне могу предположить, что после того, как завершился дочерний процесс и освободил PID, параллельно может родиться новый процесс, а потом exit code дойдет до родительского процесса, или хотя бы дойдет до родительского процесса и будет как-то обработано.
Из-за счетчика процессов вероятность того, что новый процесс займет это же PID невероятно низка из-за счетчика процессов, в котором max-pid в современных системах уже не пятизначный, но технически это наверное возможно.
Но это все слишком базовые вещи.
Если уж писать статью, то хотя бы чуть больше, чем написано в википедии. Например о том, что дерево процессов может быть не одно, что весьма полезно учитывая нынешние тренды контейнеров.
pae174
Добавлю свои две копейки. В некоторых операционных системах, например FreeBSD, PIDы назначаются случайным образом а не последовательно. Считается, что если процессы имеют случайные PID и юзер не видит список процессов другого юзера, то это более безопасно, чем простой инкремент PID.
jurikolo
Интересно, насколько сильно такой подход увеличивает нагрузку в ситуации с большим кол-вом запущенных процессов, ведь поиск свободного числа случайным образом не оптимален. В прошлом сталкивался с ситуациями, когда приложение упиралось в ограничение PID'ов, приходилось повышать лимит.
Сейчас глянул, на домашнем ноутбуке лимит по-умолчанию соответствует максимуму, то есть 4194304.
KivApple
Можно хранить список свободных pid и брать случайный элемент из него одновременно удаляя. А при завершении процесса класть его pid в список. Тогда генерация случайного pid может иметь O(1) ценой роста потребления памяти. Но хранить 32768 int на современном железе не проблема.
saboteur_kiev
большинство современных дистрибутивов из коробки уже имеют 4 млн пидов.
Линукс часто используется в ембеддед устройствах и контейнерах, каждая лишняя страница памяти на учете может быть.