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

Posted

技术标签:

【中文标题】Linux - 了解挂载命名空间和克隆 CLONE_NEWNS 标志【英文标题】:Linux - understanding the mount namespace & clone CLONE_NEWNS flag 【发布时间】:2014-05-18 07:52:17 【问题描述】:

我正在阅读 mount 和 clone 手册页。我想澄清一下 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()CLONE_NEWNS 创建子进程,这是否意味着子进程将获得树中安装点的精确副本(5 和 6)并且仍然能够访问其余部分原来的树?这是否也意味着子进程可以随意挂载 5 和 6,而不会影响在其父进程的挂载命名空间中挂载在 5 或 6 的内容。

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

谢谢。

【问题讨论】:

【参考方案1】:

进程的“挂载命名空间”就是它看到的一组挂载文件系统。一旦您从拥有一个全局挂载命名空间的传统情况转变为拥有每个进程的挂载命名空间,您必须决定在使用clone() 创建子进程时要做什么。

传统上,挂载或卸载文件系统会改变所有进程都可以看到的文件系统:有一个全局挂载命名空间,所有进程都可以看到,如果进行任何更改(例如使用mount 命令),所有进程都会立即无论它们与mount 命令的关系如何,都可以看到这种变化。

使用每进程挂载命名空间,子进程现在可以拥有与其父进程不同的挂载命名空间。现在问题来了:

子节点对挂载命名空间所做的更改是否应该传播回父节点?

很明显,这个功能必须至少是被支持的,事实上,可能必须是默认的。否则,启动 mount 命令本身不会产生任何变化(因为父 shell 看到的文件系统不会受到影响)。

同样清楚的是,这种必要的传播也必须有可能被抑制,否则我们永远无法创建挂载命名空间与其父进程不同的子进程,而我们又拥有一个全局挂载命名空间(init 看到的文件系统)。

因此,当使用clone() 创建子进程时,我们必须决定子进程是从父进程获取有关已挂载文件系统的数据的自己的副本,它可以在不影响父进程的情况下进行更改,还是获取指向与父级相同的数据结构,它可以更改(更改传播回来所必需的,就像您从 shell 启动 mount 时一样)。

如果将CLONE_NEWNS 标志传递给clone(),子进程将获得其父文件系统已挂载文件系统数据的副本,它可以在不影响父文件系统挂载命名空间的情况下对其进行更改。否则,它会获得一个 指针 指向父级的挂载数据结构,父级将看到子级所做的更改(因此mount 命令本身可以工作)。

现在,如果我使用带有 CLONE_NEWNS 的克隆来创建子进程,这是否意味着子进程将获得树(5 和 6)中安装点的精确副本,并且仍然能够访问原始树的其余部分?

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

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

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

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

没有。如果您使用了CLONE_NEWNS,则在子节点中所做的更改无法传播回父节点。

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

【讨论】:

如果启用挂载传播,子挂载和卸载事件将在挂载副本之间传播。需要(部分)禁用它才能使挂载命名空间有用。【参考方案2】:

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

AFAICU,如果一个进程创建时设置了 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;
            

如有错误请指正

【讨论】:

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

以上是关于Linux - 了解挂载命名空间和克隆 CLONE_NEWNS 标志的主要内容,如果未能解决你的问题,请参考以下文章

克隆linux虚拟机

BPM-第七章-高级OOP特征

clone()方法

存储卷的clone方式

6kvm克隆虚拟机

java克隆clone()方法和相等equals()方法的重写