前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >命名空间介绍之八:挂载命名空间和共享子树

命名空间介绍之八:挂载命名空间和共享子树

作者头像
谛听
修改2019-11-05 14:45:28
3.8K0
修改2019-11-05 14:45:28
举报
文章被收录于专栏:巫山跬步巫山跬步

挂载命名空间是创建每-用户和每-容器文件系统树的强大而灵活的工具。本文中,我们将仔细研究共享子树特性,它可通过自动、可控的方式在挂载命名空间之间传播挂载和卸载事件。

引言

挂载命名空间是第一个添加到 Linux 的命名空间类型,出现在 2002 年的 Linux 2.4.19 中。它们可隔离命名空间中的进程所看到的挂载点列表。换言之,每个挂载命名空间都有自己的挂载点列表,这意味着不同命名空间中的进程可以看到并操作单个目录层次结构的不同视图。

当系统首次启动时,有一个单一的挂载命名空间,即所谓的“初始命名空间”。带 CLONE_NEWNS 标志的 clone()(在新命名空间中创建新子进程)或 unshare()(将调用方移到新命名空间中)可创建新的挂载命名空间。当新的装挂载名空间被创建时,它将接收 clone() 或 unshare() 的调用者的命名空间的挂载点列表的拷贝。

在 clone() 或 unshare() 之后,可以在每个命名空间中独立地添加和删除挂载点(通过 mount() 和 umount() )。对挂载点列表的更改(默认情况下)仅对进程所在的挂载命名空间中的进程可见;这些更改在其他挂载命名空间中不可见。

挂载命名空间有多种用途。例如,可以提供文件系统的每个用户视图。还有其它用途,可以为新的 PID 命名空间挂载 /proc 文件系统,而不会对其它进程造成副作用,还可通过 chroot() 将进程隔离到单个目录层次结构中。在某些用例中,挂载命名空间与绑定挂载一起使用。

共享子树

挂载命名空间实现后,用户空间的程序员就遇到了一个可用性问题:挂载命名空间在命名空间之间提供了太多的隔离。例如,假设一个新磁盘加载到一个光盘驱动器中。在原来实现中,使该磁盘在所有挂载命名空间中可见的唯一方法是在每个命名空间中分别挂载该磁盘。但在许多情况下,最好仅执行一个挂载操作,就可使磁盘在系统上的所有挂载命名空间(或某些子集)中可见。

因此,共享子树特性被添加到 Linux 2.6.15 中(在 2006 年初,即大约挂载命名空间实现了三年后)。共享子树的主要优点是允许在命名空间之间自动、可控地传播挂载和卸载事件。这意味着,例如,在一个挂载命名空间中挂载一个光盘可能会使得所有其他命名空间中都挂载该光盘。

在共享子树特性下,每个挂载点都用“传播类型”标记,该类型决定在此挂载点下创建和删除的挂载点是否传播到其他挂载点。有四种不同的传播类型:

  • MS_SHARED:此挂载点与同一“对等组”中的其它挂载点共享挂载和卸载事件(下面将更详细地描述)。当在此挂载点下添加或删除挂载点时,此更改将传播到对等组,因此挂载或卸载也会发生在每个对等挂载点。传播也会反向进行,因此对等挂载上的挂载和卸载事件也会传播到此挂载点。
  • MS_PRIVATE:与共享挂载点相反。挂载点不会将事件传播到任何对等方,也不会从任何对等方接收传播事件。
  • MS_SLAVE:这种传播类型介于共享挂载和私有挂载之间。从挂载有一个主挂载---一个共享对等组,其成员将挂载和卸载事件传播到从属挂载。但是,从属挂载不会将事件传播到主挂载对等组。
  • MS_UNBINDABLE:该挂载点不可绑定。与私有挂载点一样,此挂载点不会将事件传播到对等方或接收来自对等方传播的事件。此外,此挂载点不能作为绑定挂载操作的源。

值得对上面几点进行扩展。首先,传播类型是一个每--挂载点--配置。在一个命名空间中,某些挂载点可能标记为共享,而其它挂载点则标记为私有(或从属或不可绑定)。

第二点需强调的是,传播类型决定了在挂载点下挂载和卸载事件的传播。因此,如果在共享挂载 X 下创建了子挂载 Y,则该子挂载将传播到对等组中的其它挂载点。但是,X 的传播类型不会影响在 Y 下创建和删除的挂载点;Y 下的事件的传播与否取决于对 Y 传播类型的定义。类似地,当 X 被卸载时,卸载事件是否会传播取决于其父挂载点为其定义的传播类型。

顺便说一句,也许值得澄清的是,“事件”这个词在这里被用作一个抽象的术语,意思是“发生了什么事”。事件传播的概念并不意味着在挂载点之间传递某种类型的消息。相反,它的思想是,一个挂载点上的某个挂载或卸载操作触发了一个或多个其它挂载点上的相同操作。

最后,挂载既可以是主对等组的从属,也可以与它自己的对等组(即所谓的主从和共享挂载)共享事件。在这种情况下,挂载可能从主服务器接收传播事件,然后这些事件将传播到其对等服务器。

对等组

对等组是一组挂载点,它们互相传播挂载和卸载事件。当传播类型是共享的挂载点在创建新命名空间时被复制或作为绑定挂载的源时,对等组会获得新成员。(对于绑定挂载,其细节比我们这里描述的要复杂得多;具体可查看内核源文件 Documentation/filesystems/sharedsubtree.txt )。在这两种情况下,新挂载点都会成为现有挂载点的对等组的成员。相反,挂载点在卸载时不再是对等组的成员,无论是显式的还是隐式的,如当挂载命名空间的最后一个成员进程终止或移动到另一个命名空间。

例如,假设在运行于最初挂载命名空间中的 shell 中,我们将根挂载点设为私有,并创建两个共享挂载点:

代码语言:txt
复制
sh1# mount --make-private /
sh1# mount --make-shared /dev/sda3 /X
sh1# mount --make-shared /dev/sda5 /Y

shell 中的“#” 表明,在 shell 会话中使用各种挂载命令来创建挂载点并更改其传播类型时,必须使用特权。

然后,在第二个终端上,我们使用 unshare 命令创建一个新的挂载命名空间,在其中运行 shell:

代码语言:txt
复制
sh2# unshare -m --propagation unchanged sh

(-m 选项创建一个新的挂载命名空间;后面将解释 --propagation unchanged 的用途。)

返回到第一个终端,创建一个对 /X 挂载点的绑定挂载:

代码语言:txt
复制
sh1# mkdir /Z
sh1# mount --bind /X /Z

按照这些步骤,我们将看到下图所示的情况。

在这里插入图片描述
在这里插入图片描述

在这种情况下,有两个对等组:

  • 第一个对等组包含挂载点 X、X'(挂载点 X 的副本)和 Z(对最初命名空间中源挂载点 X 的绑定挂载)。
  • 第二个对等组包含挂载点 Y 和 Y'(挂载点 Y 的副本)。

请注意,在创建第二个命名空间后才在最初命名空间中创建的绑定挂载 Z,并没有被复制到第二个命名空间,因为父挂载(/)被标记为私有。

通过 /proc/pid/mountinfo 检查传播类型和对等组

/proc/pid/mountinfo 文件(记录在 proc(5) 手册页中)显示了有关进程 PID 所在挂载命名空间中的挂载点的信息。位于同一挂载命名空间中的所有进程都将在此文件中看到相同的视图。此文件旨在提供比旧的、不可扩展的 /proc/pid/mounts 文件更多的挂载点信息。此文件中的每个记录中都包含一组(可能为空)“可选字段”,这些字段显示每个挂载的传播类型和对等组(用于共享挂载)信息。

对于共享装载,/proc/pid/mountinfo 中相应记录中的可选字段将包含 shared:N 形式的标记。这里,shared 标记表示挂载正与对等组共享传播事件。对等组由 N 标识,N 是唯一标识对等组的整数值。这些 ID 从 1 开始编号,当一个对等组不存在后还可循环使用。同一对等组的所有挂载点在 /proc/pid/mountinfo 文件中的 shared:N 标记相同。

因此,例如,如果我们在上面示例中的第一个 shell 中列出 /proc/self/mountinfo 的内容,将看到以下内容(使用 sed 过滤掉一些不相关的信息):

代码语言:txt
复制
sh1# cat /proc/self/mountinfo | sed 's/ - .*//'
61 0 8:2 / / rw,relatime
81 61 8:3 / /X rw,relatime shared:1
124 61 8:5 / /Y rw,relatime shared:2
228 61 8:3 / /Z rw,relatime shared:1

从该输出中,我们首先看到根挂载点是私有的,因为可选字段中没有任何标记。我们还看到,挂载点 /X 和 /Z 位于同一对等组(ID 为1),这意味着这两个挂载下的挂载和卸载事件将互相传播。 /Y 是另一个对等组(ID 2)中的共享装载,根据定义,它不会与对等组 1 中的挂载点相互传播事件。

还可通过 proc/pid/mountinfo 文件查看挂载点之间的父子关系。每个记录中的第一个字段是挂载点的 ID。第二个字段是父挂载的 ID。从上面的输出中,我们可以看到挂载点 /X、/Y 和 /Z 都是根挂载的子项,因为它们的父 ID 都是 61。

在第二个 shell(在第二个命名空间)中运行相同的命令,我们看到:

代码语言:txt
复制
sh2# cat /proc/self/mountinfo | sed 's/ - .*//'
147 146 8:2 / / rw,relatime
221 147 8:3 / /X rw,relatime shared:1
224 147 8:5 / /Y rw,relatime shared:2

再次,我们看到根挂载点是私有的。然后我们看到 /X 是对等组 1 中的共享挂载,与最初挂载命名空间中的挂载 /X 和 /Z 相同。最后,我们看到 /Y 是对等组 2 中的共享装载,与最初挂载名空间中的挂载 /Y 相同。最后要注意的是,在第二个命名空间中复制的挂载点有自己的 ID,与最初命名空间中相应挂载的 ID 不同。

默认值讨论

由于情况有点复杂,到目前为止,我们避免讨论新挂载点的默认传播类型。从内核的角度来看,新挂载的默认值如下:

  • 如果挂载点有父亲(即非根挂载点),并且父亲的传播类型是 MS_SHARED,则新挂载点的传播类型也是 MS_SHARED。
  • 否则,新挂载的传播类型为 MS_PRIVATE。

根据这些规则,如果根挂载是 MS_PRIVATE,默认情况下,所有子挂载也将是 MS_PRIVATE。不过,MS_SHARED 是一个更好的默认值,是更常用的传播类型。因此,systemd 将所有挂载点的传播类型设置为 MS_SHARED。所以,在大多数现代 Linux 发行版中,默认的传播类型是 MS_SHARED。不过,util-linux 的 unshare 特性却不这样认为。在创建新的挂载命名空间时,unshare 假定用户需要完全隔离的命名空间,并通过执行以下命令(该命令递归地将根目录下的所有挂载标记为私有)将所有挂载点设置为私有:

代码语言:txt
复制
mount --make-rprivate /

为了防止出现这种情况,我们可以在创建新命名空间时使用其它选项:

代码语言:txt
复制
unshare -m --propagation unchanged <cmd>

结束语

在本文中,我们介绍了挂载命名空间和共享子树的“理论”。我们现在有足够的信息来证明和理解各种传播类型的语义;这将是后续文章的主题。


原文:https://lwn.net/Articles/689856/

公众号:Geek乐园

博客:https://blog.csdn.net/u012319493/article/details/102884198

本文系外文翻译,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系外文翻译前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • 共享子树
  • 对等组
  • 通过 /proc/pid/mountinfo 检查传播类型和对等组
  • 默认值讨论
  • 结束语
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档