我试图实现以下情形:
clone()
主进程与CLONE_NEWNS
标志(这意味着新的挂载名称空间) mount()
新的文件系统 但是它并不像我预期的那样工作,我仍然在主进程中看到挂载的文件系统。 我究竟做错了什么?
来源在这里https://github.com/dmitrievanthony/sprat/blob/master/src/container.c#L47
系统默认是AWS Ubuntu,
ubuntu@ip-172-31-31-112:~/sprat$ uname -a Linux ip-172-31-31-112 4.4.0-53-generic #74-Ubuntu SMP Fri Dec 2 15:59:10 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
简短的回答:它看起来像挂载传播的类型没有正确设置。
首先,您的根挂载点的传播类型应为MS_SHARED
,由systemd在启动时设置。 这可以通过查看/proc/$PID/mountinfo
的可选字段来观察。 例如,这样的事情可能是预料之中的:
$ cat /proc/self/mountinfo . . . 25 0 8:6 / / rw,relatime shared:1 - ext4 /dev/sda6 rw,errors=remount-ro,data=ordered ^^^^^^ . . .
注意下划线(由我) shared:1
上面的shared:1
字段,表示当前/
安装点的传播类型是MS_SHARED
,并且对等组 ID是1
(在我们的例子中我们根本不关心对等组ID)。
在clone(2)
上使用CLONE_NEWNS
标志时,会创建一个新的挂载名称空间,并将其初始化为调用者挂载名称空间的副本。 新名称空间的新复制挂载点将在调用者的挂载名称空间中与其各自的原始挂载点相同的对等组加入。
父级传播类型为MS_SHARED
的新安装点的传播类型也是MS_SHARED
。 因此,当您的“包含”进程mount()
是循环设备上的文件系统时,安装默认为MS_SHARED
。 之后,它下面的所有坐骑也都会传播到“主”进程的命名空间,这就是“主”进程能看到它们的原因。
为了满足您的请求(对于“主”进程不要看到“包含”进程的挂载点),您所寻找的挂载传播类型是MS_SLAVE
或MS_PRIVATE
,这取决于您是否希望“包含”进程的根挂载点分别接收来自其他同伴的传播事件。 显然, MS_PRIVATE
提供比MS_SLAVE
更大的隔离。
因此,在你的情况下, 在安装剩余的文件系统之前 ,将“包含”进程的根挂载点的传播类型改为MS_PRIVATE
或MS_SLAVE
就MS_PRIVATE
了,因此挂载将不会被传播到“main”进程的名称空间。
首先,当“包含”进程创建其根安装点时,会尝试正确设置传播类型。
但是,我注意到在man 8 mount
(引用)中的以下内容:
请注意,Linux内核不允许使用单个安装(2)系统调用更改多个传播标志,并且这些标志不能与其他安装选项混合使用。
由于util-linux 2.23,mount命令允许一起使用多个传播标志,并且还可以与其他安装操作一起使用。 这个功能是实验性的。 当前面的安装操作成功时,传播标志由附加的安装(2)系统调用应用。
查看你的代码,“包含”过程,在它mount()
到循环设备上的文件系统之后,它向它发出chroot()
。 在这一点上,你可以通过注入这个mount(2)
调用来设置它的传播类型:
if (chroot(".") < 0) { // handle error } if (mount("/", "/", c->fstype, MS_PRIVATE, "") < 0) { // handle error } if (mkdir(...)) { // handle error }
现在传播类型被设置为MS_PRIVATE
,那么所有“包含”进程的后续安装在/
不会被传播,因此将不会在“main”进程的名称空间中可见,正如您在/proc/mounts
看到的或/proc/$PID/mountinfo
。
有关安装传播的更多信息,请参阅Linux内核的Shared Subtrees文档 。
Michael Kerrisk的优秀LWN文章比我更好地解释了mount命名空间。