我不是一个熟练的 Linux 用户,但我想更好地理解这篇文章中关于 Linux 命名空间的内容
https://stackoverflow.com/questions/44666700/unshare-pid-bin-bash-fork-cannot-allocate-memory
我想我的理解失败可能与对“命令”、“过程”以及其他一些内容的理解不足有关。
首先,让我解释一下我用于学习的一个简单实验。我打开了两个 PUTTY 终端窗口。在每个窗口上,我都执行了ssh root@[ip of machine]
。现在,我已经与 Linux 机器建立了 2 个 SSH 会话,我开始进行实验。
在第一个窗口中,我执行了以下操作:
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 开始运行后,bash 会 fork 几个新的子进程来做一些事情。”我不明白这句话的意思。所以在第二个终端窗口中,我运行了以下命令:
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 到 PID58299 是不是作者所说的“bash 将分叉几个新的子进程来做一些事情”?
背景
我首先会简单介绍一下 Linux 中进程的创建方式。我不会介绍所有选项或所有细节,而是会重点介绍关键思想。
通常,新进程是使用
fork()
系统调用创建的。成功时,fork()
将产生一个新进程,该进程运行与原始进程相同的程序(实际上,它是fork()
在调用时调用的程序的克隆fork()
)。该fork()
函数在“父”进程和“子”(新创建的)进程中都返回。每个进程都可以检查的返回值fork()
以确定它们是“父”还是“子”,并且它们可以使用它来决定下一步要做什么。通常,创建新进程意味着我们要运行不同的程序,而到目前为止,我们只有一种方法可以创建同一程序的副本。幸运的是,有一个单独的系统调用,
exec()
可以将当前正在运行的程序替换为新程序。考虑一下您有一个 shell 的情况(我
bash
在这里假设),并且您键入ls
以列出当前目录的内容:问题
你从以下开始:
注意错误
bash: fork: Cannot allocate memory
——这不是一个好兆头。在这种情况下,
unshare
程序 (1) 创建一个新的 PID 命名空间和 (2)exec
s/bin/bash
。回想一下背景部分,用新程序 ( )exec
替换当前正在运行的进程( ) – 它不会创建新进程。unshare
/bin/bash
到目前为止,新创建的 PID 命名空间中还没有运行任何进程。命名空间存在,但创建命名空间的进程尚未
fork()
执行任何操作。当
bash
开始运行时,它通常会运行一些程序。这run
是背景部分中描述的fork
/exec
组合。内核将第一个bash
fork()
进入新 PID 命名空间的进程放置在该命名空间中,该进程将成为init
该命名空间的进程(该命名空间中 pid = 1 的进程)。运行的程序bash
可能寿命很短,因此它会运行、终止,然后 PID 命名空间被销毁。接下来
bash
尝试运行其他命令。它想将这些命令放入新的 PID 命名空间中,但该 PID 命名空间不再存在。结果,fork()
失败导致您看到的错误消息。如果您尝试运行任何其他命令,您将再次看到它:解决方案
正如您在问题中提到的,该
unshare
程序还有另一个选项,在这个场景中很有用。来自man unshare
:您可以用以下命令替换第一个命令:
请注意,在这种情况下没有错误。
此选项会导致
unshare
其行为发生变化。它不会立即使用exec()
来替换自身/bin/bash
,而是使用上面背景部分中描述的fork()
/行为:exec()
您可以通过打印其进程 ID 来确认此例中
/bin/bash
是init
进程(即 pid 为 1 的进程):问题的答案
第二个窗口没有显示任何的迹象,
unshare
因为它不再运行——它曾经用exec()
替换自身/bin/bash
。该
--fork
选项改变了 的行为unshare
,以便它fork()
首先使用 创建一个新进程(新创建的 PID 命名空间中的进程),然后该进程使用exec()
替换自身/bin/bash
。新的子流程很可能是短暂的,因此当您运行时它们不再运行
ps
。