我正在尝试使用具有 root 访问权限的 Debian jessie 服务器来了解 Linux 命名空间。
考虑这个 C 代码:
# /tmp/test.c
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
static char child_stack[1048576];
static int my_child() {
system("/bin/bash");
}
int main() {
pid_t child_pid = clone(my_child, child_stack+1048576,
CLONE_NEWPID | CLONE_NEWNS | SIGCHLD, NULL);
waitpid(child_pid, NULL, 0);
return 0;
}
接下来,我在单个终端的单个会话中运行这些命令:
/tmp# id
uid=0(root) gid=0(root) groups=0(root),1093867019
/tmp# echo $$
1804
/tmp# ps -eaf | head
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 May08 ? 00:00:01 /sbin/init
root 2 0 0 May08 ? 00:00:00 [kthreadd]
root 3 2 0 May08 ? 00:00:00 [ksoftirqd/0]
root 5 2 0 May08 ? 00:00:00 [kworker/0:0H]
root 7 2 0 May08 ? 00:00:11 [rcu_sched]
root 8 2 0 May08 ? 00:00:00 [rcu_bh]
root 9 2 0 May08 ? 00:00:00 [migration/0]
root 10 2 0 May08 ? 00:00:00 [watchdog/0]
root 11 2 0 May08 ? 00:00:00 [khelper]
/tmp# grep /proc /proc/$$/mountinfo
15 19 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw
33 15 0:29 / /proc/sys/fs/binfmt_misc rw,relatime shared:20 - autofs systemd-1 rw,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
/tmp# gcc test.c
/tmp# ./a.out
/tmp# echo $$
2
/tmp# echo $PPID
1
/tmp# ps -eaf | head
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 May08 ? 00:00:01 /sbin/init
root 2 0 0 May08 ? 00:00:00 [kthreadd]
root 3 2 0 May08 ? 00:00:00 [ksoftirqd/0]
root 5 2 0 May08 ? 00:00:00 [kworker/0:0H]
root 7 2 0 May08 ? 00:00:11 [rcu_sched]
root 8 2 0 May08 ? 00:00:00 [rcu_bh]
root 9 2 0 May08 ? 00:00:00 [migration/0]
root 10 2 0 May08 ? 00:00:00 [watchdog/0]
root 11 2 0 May08 ? 00:00:00 [khelper]
tmp# grep /proc /proc/$$/mountinfo
15 19 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw
33 15 0:29 / /proc/sys/fs/binfmt_misc rw,relatime shared:20 - autofs systemd-1 rw,fd=22,pgrp=0,timeout=300,minproto=5,maxproto=5,direct
/tmp# mount -t proc proc /proc
/tmp# grep /proc /proc/$$/mountinfo
92 70 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw
93 92 0:29 / /proc/sys/fs/binfmt_misc rw,relatime shared:20 - autofs systemd-1 rw,fd=22,pgrp=0,timeout=300,minproto=5,maxproto=5,direct
97 92 0:34 / /proc rw,relatime shared:27 - proc proc rw
/tmp# ps -eaf
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 07:31 pts/0 00:00:00 ./a.out
root 2 1 0 07:31 pts/0 00:00:00 /bin/bash
root 14 2 0 07:31 pts/0 00:00:00 ps -eaf
/tmp# exit
exit
/tmp# echo $$
1804
/tmp# grep /proc /proc/$$/mountinfo
grep: /proc/1804/mountinfo: No such file or directory
/tmp# ps -eaf
Error, do this: mount -t proc proc /proc
/tmp# mount -t proc proc /proc
/tmp# grep /proc /proc/$$/mountinfo
15 19 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:12 - proc proc rw
33 15 0:29 / /proc/sys/fs/binfmt_misc rw,relatime shared:20 - autofs systemd-1 rw,fd=22,pgrp=1,timeout=300,minproto=5,maxproto=5,direct
98 15 0:34 / /proc rw,relatime shared:27 - proc proc rw
69 98 0:3 / /proc rw,relatime shared:28 - proc proc rw
为什么子进程退出后,/proc
没有挂载?子进程对挂载点所做的任何更改不应该影响父进程的挂载点吗?这似乎与埃米特对这个问题的回答相矛盾https://stackoverflow.com/questions/22889241/linux-understanding-the-mount-namespace-clone-clone-newns-flag。
答案1
clone
当使用该标志创建子进程时CLONE_NEWNS
,子进程将获得自己的挂载命名空间。子命名空间中的挂载操作(mount
、umount
、mount --bind
等)仅在该命名空间内有效,而父命名空间中的挂载操作仅在新命名空间外有效。
共享安装除外。安装座可以是共享,在这种情况下,操作会影响共享挂载的所有命名空间。共享挂载的典型用例是使可移动驱动器在子命名空间(例如 chroot)中可用。关系类型更多(私有坐骑、不可绑定坐骑);有关更多详细信息,请参阅内核文档。
您可以通过检查来检查挂载是否共享:如果该行包含,则挂载是共享的,并且该数字是标识其共享的命名空间集的唯一值。如果该行不包含此类指示,则该安装是私有的。/proc/PID/mountinfo
shared:NUMBER
在您的系统上,/proc
是共享的。当您在子命名空间中挂载 proc 的新实例时,由于您是在父命名空间上挂载的/proc
,因此该新实例也是共享的,因此它在子命名空间和父命名空间中都是可见的。当您退出子命名空间时,第二个实例/proc
仍保持挂载状态,因为它与仍然活动的父命名空间共享。
有两件事使您的场景变得复杂:您还创建了一个 PID 命名空间,并且将/proc
两者用作实验的主题和观察手段。当ps
抱怨时/proc
未安装时,它实际上显示了一条误导性的错误消息 -错误的 proc
已安装(aproc
表示错误的命名空间)。您可以使用ls /proc
和来观察这一点cat /proc/1/mountinfo
。我建议使用临时文件系统进行实验,这样会更容易理解发生了什么。
父# ./a.out 孩子#回声$$ 2 子# ls /proc 这是父级的 proc,/proc/PID 在父级 PID 命名空间中 孩子# ps 1 … 在里面 子# mount -t proc proc /proc 现在子挂载命名空间中的 /proc 用于子 PID 命名空间 孩子# ps 1 …a. 输出 孩子#退出 父级#
到目前为止,私有还是共享并不重要/proc
,但现在重要了。如果/proc
是私有的,那么此时我们正在观察父级的/proc
,它从未受到影响并显示 PID 命名空间。但如果/proc
是共享的,那么mount
我们之前发出的命令会影响两个命名空间,因此:
父# ls /proc acpi 声音 buddyinfo ... 父级# ps 1 错误,请执行以下操作:mount -t proc proc /proc 实际上,/proc 已挂载,但它是我们之前创建的 PID 命名空间的 proc,现在运行的进程为零。 父# grep -c ' /proc ' /proc/mounts 2 父级#卸载/proc 我们已经卸载了隐藏父命名空间 /proc 的子 PID 命名空间的 /proc,因此“正常”/proc 再次可见。 父级# ps 1 … 在里面