- Вопрос или проблема
- Предыстория
- Проблема
- Решение
- Ответы на ваши вопросы
- Ответ или решение
- Понимание концепций команд, процессов и пространств имен в Linux
- Что такое команда и процесс?
- Пространства имен в Linux
- Ваш эксперимент и его анализ
- 1. Почему отсутствует unshare во втором окне?
- 2. Что означает "bash будет создавать несколько новых подпроцессов"?
- Заключение
Вопрос или проблема
Я не сильный пользователь Linux, но хочу лучше понять материал в этом посте, который говорит о пространствах имен Linux.
https://stackoverflow.com/questions/44666700/unshare-pid-bin-bash-fork-cannot-allocate-memory
Я думаю, что моя неспособность понять может быть связана с недостаточным пониманием понятий «команда», «процесс» и, возможно, нескольких других вещей.
Сначала позвольте объяснить простой эксперимент, который я использую для своего образования. Я открыл два окна терминала PUTTY. В каждом окне я выполнил ssh root@[ip машины]
. Теперь, когда у меня есть 2 SSH-сессии к моему Linux-устройству, я начинаю свои эксперименты.
В первом окне я сделал это:
root@localhost:~# unshare --pid /bin/bash
bash: fork: Cannot allocate memory
Во втором окне я сделал это:
root@localhost:~# ps -aux | grep unshare
root 58188 0.0 0.0 6480 2284 pts/3 R+ 21:49 0:00 grep --color=auto unshare
Вот мои вопросы:
-
Во втором окне нет никаких указаний на
unshare --pid /bin/bash
. Это связано с тем, что команда/bin/bash
или процесс/bin/bash
уже завершились? Именно поэтому многие пользователи Linux в интернете рекомендуют использовать--fork
, чтобы/bin/bash
запускался в вновь созданном пространстве имен? -
В принятых ответах сказано следующее: “После запуска bash он создаст несколько новых подпроцессов для выполнения некоторых действий.” Я не понимаю значение этого предложения. Итак, во втором окне терминала я выполнил это:
root@localhost:~# unshare -pf /bin/bash
root@localhost:~# ps -a
PID TTY TIME CMD
58278 pts/2 00:00:00 sudo
58279 pts/2 00:00:00 su
58280 pts/2 00:00:00 bash
58291 pts/2 00:00:00 unshare
58292 pts/2 00:00:00 bash
58299 pts/2 00:00:00 ps
Являются ли PID 58278 и PID 58299 тем, что автор имел в виду, говоря о том, что “bash создаст несколько новых подпроцессов для выполнения некоторых действий”?
Предыстория
Я начну с некоторой упрощенной предыстории о том, как создаются процессы в Linux. Я не буду охватывать все варианты или все детали, а вместо этого сосредоточусь на ключевых идеях.
В общем, новые процессы создаются с помощью системного вызова fork()
. При успешном выполнении fork()
создается новый процесс, который выполняет ту же программу, что и оригинал (по сути, это клон программы, которая вызвала fork()
в момент вызова fork()
). Функция fork()
возвращает значение как в «родительском» процессе, так и в «дочернем» (только что созданном) процессе. Каждый процесс может проверить возвращаемое значение fork()
, чтобы определить, он “родитель” или “дочь”, и использовать это для принятия решения о том, что делать дальше.
Часто создание нового процесса означает, что мы хотим запустить другую программу, и до сих пор у нас есть только способ создавать копии одной и той же программы. К счастью, есть отдельный системный вызов, exec()
, который заменяет выполняемую программу на новую программу.
Рассмотрим случай, когда у вас есть оболочка (я предполагаю bash
), и вы набираете ls
, чтобы отобразить содержимое текущего каталога:
(P1:bash) вызывает fork()
--- ядро создает P2, который является копией P1
--- ядро начинает выполнять P2
(P1:bash) fork() возвращает PID P2, поэтому он знает, что это родительский процесс
(P1:bash) Ждет, пока P2 не завершится (подробное опущено)
(P2:bash) fork() возвращает 0, поэтому он знает, что это дочерний процесс
(P2:bash) вызывает exec("ls")
--- ядро заменяет bash на ls в P2 и начинает выполнять ls
(P2:ls) начинает выполняться
...
(P2:ls) в конечном итоге завершается
(P1:bash) пробуждается, так как P2 завершился, и продолжает свои дела
Проблема
Вы начинаете с:
# unshare --pid /bin/bash
bash: fork: Cannot allocate memory
bash-5.2#
Обратите внимание на ошибку bash: fork: Cannot allocate memory
— это плохой знак.
В этом случае программа unshare
(1) создает новое пространство имен PID и (2) exec
вызывает /bin/bash
. Напомню из раздела Предыстория, что exec
заменяет текущий выполняемый процесс (unshare
) на новую программу (/bin/bash
) — он не создает новый процесс.
До сих пор в вновь созданном пространстве имен PID не выполняется ни один процесс. Пространство имен существует, но процесс, создавший пространство имен, еще не fork()
создал ничего.
Когда bash
начинает выполняться, он обычно запускает некоторый набор программ. Здесь run
представляет собой комбинацию fork
/exec
, описанную в разделе Предыстория. Ядро помещает первый процесс, который bash
создает через fork()
, в новое пространство имен PID, и этот процесс становится процессом init
для этого пространства имен (процесс в этом пространстве имен с pid = 1). Программа, которую запускает bash
, вероятно, кратковременна, поэтому она выполняется, завершается, и пространство имен PID уничтожается.
Затем bash
пытается выполнить какую-то другую команду. Он хочет поместить эти команды в новое пространство имен PID, но это пространство имен PID больше не существует. В результате fork()
завершается неудачей, что приводит к сообщению об ошибке, которое вы видите. Вы увидите это снова, если попытаетесь выполнить любую другую команду:
bash-5.2# ls
bash: fork: Cannot allocate memory
bash-5.2#
Решение
Как вы заметили в своем вопросе, у программы unshare
есть другой параметр, который полезен в этом сценарии. Из man unshare
:
-f, --fork
Создает указанный процесс в качестве дочернего процесса
unshare
, а не запускает его напрямую. Это полезно при создании нового пространства имен PID. Обратите внимание, что когдаunshare
ждет завершения дочернего процесса, он игнорируетSIGINT
иSIGTERM
и не передает сигналы дочернему процессу. Необходимо отправлять сигналы дочернему процессу.
Вы можете заменить вашу первую команду на:
# unshare --fork --pid /bin/bash
#
Обратите внимание, что в этом случае нет ошибки.
Этот параметр изменяет поведение unshare
. Вместо того чтобы немедленно использовать exec()
для замены себя на /bin/bash
, он использует поведение fork()
/exec()
, описанное в разделе Предыстория выше:
(P1:unshare) вызывает fork()
--- ядро создает P2, который является копией P1
--- ядро начинает выполнять P2
(P1:unshare) fork() возвращает PID P2, поэтому он знает, что это родительский процесс
(P1:unshare) Ждет, пока P2 не завершится (подробное опущено)
(P2:unshare) fork() возвращает 0, поэтому он знает, что это дочерний процесс.
--- P2 выполняется в новом пространстве имен PID и имеет pid = 1
(P2:unshare) вызывает exec("/bin/bash")
--- ядро заменяет unshare на /bin/bash в P2 и начинает выполнять bash
(P2:bash) начинает выполняться
Вы можете подтвердить, что в этом случае /bin/bash
является процессом init
(т.е. процессом с pid 1), напечатав его идентификатор процесса:
# echo $$
1
#
Ответы на ваши вопросы
- Во втором окне нет никаких указаний на
unshare --pid /bin/bash
. Это связано с тем, что команда/bin/bash
или процесс/bin/bash
уже завершились? Именно поэтому многие пользователи Linux в интернете рекомендуют использовать--fork
, чтобы/bin/bash
запускался в вновь созданном пространстве имен?
Во втором окне нет никаких указаний на unshare
, потому что он больше не выполняется — он использовал exec()
, чтобы заменить себя на /bin/bash
.
Параметр --fork
изменяет поведение unshare
, так что он использует fork()
, чтобы сначала создать новый процесс — процесс в вновь созданном пространстве имен PID — затем этот процесс использует exec()
, чтобы заменить себя на /bin/bash
.
- В принятых ответах сказано следующее: “После запуска bash он создаст несколько новых подпроцессов для выполнения некоторых действий.” Я не понимаю значение этого предложения. Итак, во втором окне терминала я выполнил это:
Новые подпроцессы, вероятно, недолговечны, поэтому они уже не выполняются к моменту, когда вы запускаете ps
.
Ответ или решение
Понимание концепций команд, процессов и пространств имен в Linux
При изучении Linux, особенно таких тем, как пространства имен и управление процессами, важно упорядочить и понять ключевые концепции, такие как команды, процессы и пространства имен. Давайте разберем эти аспекты более подробно.
Что такое команда и процесс?
Команда — это выражение, которое вы вводите в оболочку (например, bash) для выполнения какой-либо задачи. Например, команда ls
выводит список файлов в текущем каталоге. Команда может включать не только саму утилиту, но и аргументы, которые передаются ей для выполнения.
Процесс — это экземпляр программы, которая выполняется в операционной системе. Каждый раз, когда команда исполняется, система создает новый процесс. Процесс имеет уникальный идентификатор — PID (Process ID), а также набор ресурсов, таких как память и время процессора.
Пространства имен в Linux
Пространства имен — это механизм, позволяющий изолировать ресурсы процессов. С помощью пространств имен можно создать «виртуальные» окружения, в которых одни и те же идентификаторы процессов могут использоваться для разных процессов. Это важный инструмент при разработке контейнеров и систем виртуализации, поскольку он позволяет размещать процессы в изолированном окружении.
Ваш эксперимент и его анализ
1. Почему отсутствует unshare
во втором окне?
Ваше наблюдение о том, что команда unshare --pid /bin/bash
не отображается во втором окне терминала, связано с естественным поведением системы. Когда команда unshare
выполняется, она создает новый PID-namespace и затем заменяет себя на bash
с помощью системного вызова exec()
. Это означает, что текущий процесс unshare
завершает свое существование и заменяется процессом bash
.
Отсутствие видимости этого процесса в другом терминале сигнализирует о том, что процесс unshare
больше не существует, что и объясняет вашу проблему с ошибкой «Cannot allocate memory». Чтобы избежать этой ситуации, и как вы заметили, рекомендуется использовать флаг --fork
. Это позволяет команду запустить как дочерний процесс в новом пространстве имен, таким образом сохраняя сам процесс unshare
активным.
2. Что означает "bash будет создавать несколько новых подпроцессов"?
При запуске bash
он часто взаимодействует с различными программами, такими как обработчики команд или утилиты, которые выполняют определенные функции (например, sudo
, ps
и т.д.). Однако, как правило, эти подпроцессы являются короткоживущими, и они могут завершаться до того, как вы успеете их увидеть.
Например, когда вы вызываете ps -a
, как в вашем тесте, вы можете увидеть идентификаторы процессов, созданных bash
, но если они продолжают завершаться быстро, вы можете не увидеть всех промежуточных процессов, созданных в результате выполнения команд внутри bash
.
Заключение
Понимание этих базовых концепций и механизмов важно для более глубокого изучения работы операционной системы Linux. С помощью пространства имен вы можете изолировать процессы и управлять их выполнением в безопасном окружении, что особенно полезно в контейнеризации и виртуализации. Продолжайте изучать, экспериментировать и задавайте вопросы — это лучший способ наладить уверенное обращение с Linux!
Если у вас есть вопросы или вам требуется помощь в дальнейших экспериментах с пространствами имен и управления процессами, не стесняйтесь обращаться.