unshare --pid /bin/bash - fork cannot allocate memory unshare命令--pid选项:fork无法分配内存

39

我正在尝试使用Linux命名空间,特别是pid命名空间。

我想用bash测试一些东西,但遇到了这个问题:

unshare -p /bin/bash
bash: fork: Cannot allocate memory

从那里运行ls会导致核心转储。退出是唯一可能的事情。

为什么会这样做?

2个回答

61

这个错误是由于PID 1进程在新的命名空间中退出所致。

在bash开始运行后,bash会fork出多个子进程来执行一些任务。如果您运行unshare而没有使用-f选项,则bash将拥有与当前"unshare"进程相同的PID。当前的"unshare"进程调用unshare systemcall,创建一个新的pid命名空间,但当前的"unshare"进程不在新的pid命名空间中。这是Linux内核的预期行为:进程A创建一个新的命名空间,进程A本身不会被放入新的命名空间中,只有进程A的子进程会被放入新的命名空间中。因此,当您运行:

unshare -p /bin/bash

unshare进程将执行/bin/bash,并且/bin/bash将fork出多个子进程,bash的第一个子进程将成为新命名空间的PID 1,然后子进程将在完成其任务后退出。因此,新命名空间的PID 1将退出。

PID 1进程具有特殊功能:它应该成为所有孤立进程的父进程。如果根命名空间中的PID 1进程退出,内核将崩溃。如果子命名空间中的PID 1进程退出,Linux内核将调用disable_pid_allocation函数,在该命名空间中清除PIDNS_HASH_ADDING标志。当Linux内核创建新进程时,内核将调用alloc_pid函数在命名空间中分配一个PID,如果未设置PIDNS_HASH_ADDING标志,则alloc_pid函数将返回-ENOMEM错误。这就是您得到“无法分配内存”的原因。

您可以通过使用'-f'选项来解决此问题:

unshare -fp /bin/bash

如果您使用'-f'选项运行unshare,unshare将在创建新的pid命名空间后fork出一个新进程,并在新进程中运行/bin/bash。新进程将成为新pid命名空间的PID 1。然后bash也会fork出几个子进程来执行一些任务。由于bash本身是新pid命名空间的PID 1,其子进程可以退出而没有任何问题。


4
为了支持这个非常有帮助的答案,这里提供一些手册页的引用:man 2 unshare中关于CLONE_NEWPID的说明为:“取消共享PID命名空间,使调用进程拥有一个新的PID命名空间,用于其子进程,该命名空间与任何先前存在的进程都不共享。调用进程不会移动到新的命名空间中。由调用进程创建的第一个子进程将具有进程ID 1 并在新的命名空间中扮演init(1)的角色。” - nh2
这是Linux内核的期望行为:进程A创建一个新的命名空间,进程A本身不会被放入新的命名空间,只有进程A的子进程才会被放入新的命名空间。这个说法仅适用于PID命名空间吗?它是否适用于其他命名空间,如挂载? - Shabirmean
@yupeng0921,"unshare -fp /bin/bash" 运行正常。但是在之后执行 ps -e 命令时,我可以看到来自主机的所有进程。根据说明,我应该只看到 PID 为 1 的 unshare 进程和一些 bash 进程正在运行。但事实并非如此。您能解释一下吗? - Amit Bhaira
1
@AmitBhaira 您的ps可能通过读取/proc工作,而您仍然挂载了父名称空间的/proc。请使用 --mount-proc - David

14

这并没有解释为什么会发生这种情况,但是展示了如何在新的pid命名空间中正确启动一个shell:

使用-f标志从unshare中分离出一个新的shell,以便新的shell在新创建的命名空间中获得PID 1:

unshare -fp /bin/bash

您可能还想传递--mount-proc选项,以便您的ps列表反映您新创建的PID命名空间而不是父PID命名空间:

unshare -fp --mount-proc /bin/bash

现在运行ps命令:

# ps
   PID TTY          TIME CMD
 1 pts/1    00:00:00 bash
11 pts/1    00:00:00 ps

很高兴看到它有所帮助! - hek2mgl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接