Понимание концепций команд, процессов и пространств имен

Вопрос или проблема

Я не сильный пользователь 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

Вот мои вопросы:

  1. Во втором окне нет никаких указаний на unshare --pid /bin/bash. Это связано с тем, что команда /bin/bash или процесс /bin/bash уже завершились? Именно поэтому многие пользователи Linux в интернете рекомендуют использовать --fork, чтобы /bin/bash запускался в вновь созданном пространстве имен?

  2. В принятых ответах сказано следующее: “После запуска 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
#

Ответы на ваши вопросы

  1. Во втором окне нет никаких указаний на unshare --pid /bin/bash. Это связано с тем, что команда /bin/bash или процесс /bin/bash уже завершились? Именно поэтому многие пользователи Linux в интернете рекомендуют использовать --fork, чтобы /bin/bash запускался в вновь созданном пространстве имен?

Во втором окне нет никаких указаний на unshare, потому что он больше не выполняется — он использовал exec(), чтобы заменить себя на /bin/bash.

Параметр --fork изменяет поведение unshare, так что он использует fork(), чтобы сначала создать новый процесс — процесс в вновь созданном пространстве имен PID — затем этот процесс использует exec(), чтобы заменить себя на /bin/bash.

  1. В принятых ответах сказано следующее: “После запуска 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!

Если у вас есть вопросы или вам требуется помощь в дальнейших экспериментах с пространствами имен и управления процессами, не стесняйтесь обращаться.

Оцените материал
Добавить комментарий

Капча загружается...