Linux - 理解挂载命名空间和克隆CLONE_NEWNS标志

26
我正在阅读mountclone手册。我想澄清CLONE_NEWNS如何影响子进程对文件系统的视图。
(文件层次结构)
让我们把这棵树看作目录层次结构。假设5和6是父进程中的挂载点。我在另一个question中澄清了挂载点。
所以我的理解是:5和6是挂载点,这意味着先前使用了mount命令在5和6处“挂载”文件系统(目录层次结构)(这意味着5和6下也必须有目录树)。
来自mount手册页:
 A mount namespace is the set of filesystem mounts that are visible to a process. 

clone手册页面中得知:
Every process lives in a mount namespace.  The namespace of a process is the data 
(the set of mounts) describing the file hierarchy as seen by that process.  After 
a fork(2) or clone() where the CLONE_NEWNS flag is not set, the child lives in the 
same mount namespace as the parent.

此外:
After a clone() where the CLONE_NEWNS flag is set, the cloned child is started in a 
new mount namespace, initialized with a copy of the namespace of the parent.

现在如果我使用带有CLONE_NEWNS的clone()来创建一个子进程,这是否意味着子进程将获得树中挂载点(5和6)的精确副本,并仍然能够访问原始树的其余部分?这是否也意味着子进程可以随意挂载5和6,而不影响父进程挂载命名空间中挂载在5或6的内容?
如果是,这是否还意味着子进程可以挂载/卸载与5或6不同的目录,并影响父进程可见的内容?
谢谢。
2个回答

31
一个进程的“mount namespace”就是它所看到的挂载文件系统。从传统的全局命名空间到每个进程拥有自己的挂载命名空间后,当用clone()创建一个子进程时,必须决定如何处理。
传统上,挂载或卸载文件系统会更改所有进程看到的文件系统:有一个全局的挂载命名空间,所有进程都能看到,如果做出任何更改(例如使用mount命令),所有进程都将立即看到该更改,而不考虑它们与mount命令的关系。
现在,由于每个进程有自己的挂载命名空间,因此一个子进程可以与其父进程具有不同的挂载命名空间。现在问题来了:
子进程对挂载命名空间所做的更改是否要传递回父进程?
显然,这种功能至少必须得到支持,并且可能必须成为默认设置。否则,启动mount命令本身将不会产生任何更改(因为父 shell 看到的文件系统不受影响)。
同样明确的是,必须也可能抑制这种必要的传递,否则我们永远无法创建一个挂载命名空间不同于其父进程的子进程,我们又有了一个全局的挂载命名空间(init所看到的文件系统)。
因此,当使用clone()创建子进程时,必须决定该子进程是从父进程获得已挂载文件系统数据的副本,这样它可以对其进行更改而不影响父进程,还是获得与父进程相同数据结构的指针,这使得更改能够传播回来(例如从 shell 启动mount命令)。如果将CLONE_NEWNS标志传递给clone(),则子进程会获取其父进程挂载的文件系统数据的副本,它可以更改而不会影响父进程的挂载命名空间。否则,它将获得指向父进程挂载数据结构的指针,子进程所做的更改将被父进程看到(因此mount命令本身可以工作)。

现在,如果我使用带有CLONE_NEWNS的clone创建一个子进程,这是否意味着子进程将获得树中挂载点(5和6)的完全副本,并仍能访问原始树的其余部分?

是的,在调用clone()后,它看到与父进程完全相同的树。

这是否也意味着子进程可以随意挂载/卸载5和6,而不会影响父进程挂载命名空间中挂载在5或6上的内容?

是的。由于您使用了CLONE_NEWNS,因此子进程可以从5卸载一个设备并在那里挂载另一个设备,只有它(和它的孩子)才能看到更改。在这种情况下,没有其他进程可以看到子进程所做的更改。

如果是的,这是否也意味着子进程可以挂载/卸载与5或6不同的目录,并影响父进程可见的内容?

不是。如果您使用了CLONE_NEWNS,则子进程中所做的更改无法向父进程传播。

如果您没有使用CLONE_NEWNS,则子进程将收到指向与其父进程相同的挂载命名空间数据的指针,任何共享这些数据结构的进程(包括父进程)都将看到子进程所做的任何更改。(在使用fork()创建新子进程时也是如此。)


1
如果启用了挂载传播,子挂载和卸载事件将在挂载的副本之间传播。为了使挂载命名空间有用,它需要被(部分)禁用。 - Timothy Baldwin

6

我没有足够的声望点来添加评论,所以将这个评论作为答案添加。 这只是对Emmet答案的补充。

据我所知,如果使用CLONE_NEWNS标志创建进程,则只能挂载设置了FS_USERNS_MOUNT标志的文件系统。几乎所有基于磁盘的文件系统都不设置此标志(出于安全原因)。 在do_new_mount中,有这个检查:

        if (user_ns != &init_user_ns) {
            if (!(type->fs_flags & FS_USERNS_MOUNT)) {
                    put_filesystem(type);
                    return -EPERM;
            }

如果我错了,请纠正我


4
你错了。FS_USERNS_MOUNT 的意思是文件系统可以由被映射为 root 用户在用户 (uid) 命名空间中的非 root 用户挂载。也就是说,如果你是真正的 root,你可以在 CLONE_NEWNS 挂载命名空间中挂载任何文件系统。 - nukemberg

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