前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >命名空间介绍之九:命名空间,挂载传播和不可绑定挂载

命名空间介绍之九:命名空间,挂载传播和不可绑定挂载

作者头像
谛听
修改2019-11-17 01:01:36
2.4K0
修改2019-11-17 01:01:36
举报
文章被收录于专栏:巫山跬步巫山跬步

在上一期命名空间系列的文章中,我们研究了挂载命名空间和共享子树的基本概念,包括挂载传播类型和对等组的概念。在这篇文章中,我们提供了各种传播类型操作的一些实际演示:MS_SHARED,MS_PRIVATE,MS_SLAVE 和 MS_UNBINDABLE。

MS_SHARED 和 MS_PRIVATE 示例

正如在上一篇文章中看到的,MS_SHARED 和 MS_PRIVATE 传播类型大致相反。共享挂载点是对等组的成员。对等组中的挂载点之间互相传递挂载和卸载事件。相比之下,私有挂载点不属于对等组;它既不向对等方传播事件,也不从对等方接收事件。在下面的 shell 会话中,我们将演示这两种传播类型的不同之处。

假设在最初的挂载命名空间中,我们已经有两个挂载点,/mntS 和 /mntP。在命名空间中的 shell 中,我们将 /mntS 标记为共享,将 /mntP 标记为私有,并在 /proc/self/mountinfo 中查看这些挂载:

代码语言:txt
复制
sh1# mount --make-shared /mntS
sh1# mount --make-private /mntP
sh1# cat /proc/self/mountinfo | grep '/mnt' | sed 's/ - .*//'
77 61 8:17 / /mntS rw,relatime shared:1
83 61 8:15 / /mntP rw,relatime

从输出中,我们看到 /mntS 是对等组 1 中的共享挂载,而 /mntP 没有标记,这表明它是私有挂载。(如前一篇文章所述,大多数挂载和卸载操作都要求用户具有特权,如“#”提示符所示。)

在第二个终端中,我们创建一个新的挂载命名空间,在其中运行第二个 shell 并检查挂载:

代码语言:txt
复制
sh2# unshare -m --propagation unchanged sh
sh2# cat /proc/self/mountinfo | grep '/mnt' | sed 's/ - .*//'
222 145 8:17 / /mntS rw,relatime shared:1
225 145 8:15 / /mntP rw,relatime

新的挂载命名空间得到了最初挂载命名空间的挂载点的拷贝。这些新的挂载点保持相同的传播类型,但具有唯一的挂载 ID(记录中的第一个字段)。

在第二个终端中,我们在 /mntS 和 /mntP 下创建挂载并检查结果:

代码语言:txt
复制
sh2# mkdir /mntS/a
sh2# mount /dev/sdb6 /mntS/a
sh2# mkdir /mntP/b
sh2# mount /dev/sdb7 /mntP/b
sh2# cat /proc/self/mountinfo | grep '/mnt' | sed 's/ - .*//'
222 145 8:17 / /mntS rw,relatime shared:1
225 145 8:15 / /mntP rw,relatime
178 222 8:22 / /mntS/a rw,relatime shared:2
230 225 8:23 / /mntP/b rw,relatime

从上面可以看出,/mntS/a 为共享的(从其父挂载继承此设置),而 /mntP/b 为私有挂载。

返回到第一个终端并检查设置,我们看到在共享挂载点 /mntS 下创建的新挂载传播到其对等挂载(位于最初挂载命名空间中),但在私有挂载点 /mntP 下创建的新挂载没有传播:

代码语言:txt
复制
sh1# cat /proc/self/mountinfo | grep '/mnt' | sed 's/ - .*//'
77 61 8:17 / /mntS rw,relatime shared:1
83 61 8:15 / /mntP rw,relatime
179 77 8:22 / /mntS/a rw,relatime shared:2

MS_SLAVE 示例

将挂载点设为从节点,可让它收到主对等组的挂载和卸载事件,同时防止它将事件传播到该主节点。如果我们希望(比方说)在主对等组(在另一个装载命名空间中)中挂载光盘时接收挂载事件,但希望防止从属挂载下的挂载和卸载事件在其他命名空间中产生副作用,则这非常有用。

通过将最初挂载命名空间中的两个(现有)挂载点标记为共享来演示从属行为的效果:

代码语言:txt
复制
sh1# mount --make-shared /mntX
sh1# mount --make-shared /mntY
sh1# cat /proc/self/mountinfo | grep '/mnt' | sed 's/ - .*//'
132 83 8:23 / /mntX rw,relatime shared:1
133 83 8:22 / /mntY rw,relatime shared:2

在第二个终端上,我们创建一个新的挂命名空间并检查复制后的挂载点:

代码语言:txt
复制
sh2# unshare -m --propagation unchanged sh
sh2# cat /proc/self/mountinfo | grep '/mnt' | sed 's/ - .*//'
168 167 8:23 / /mntX rw,relatime shared:1
169 167 8:22 / /mntY rw,relatime shared:2

在新的挂载命名空间中,我们将其中一个挂载点标记为从属。将共享挂载更改为从属挂载的效果是,使其成为其以前所属对等组的从属。

代码语言:txt
复制
sh2# mount --make-slave /mntY
sh2# cat /proc/self/mountinfo | grep '/mnt' | sed 's/ - .*//'
168 167 8:23 / /mntX rw,relatime shared:1
169 167 8:22 / /mntY rw,relatime master:2

在上述输出中,/mntY 挂载点用 master:2 标记。标记名可能有悖常理:它表示挂载点是从挂载,从 ID 为 2 的主对等组接收传播事件。如果一个挂载既是另一个对等组的从属,又与它自己的对等组共享事件,那么 /proc/pid/mountinfo 记录中的可选字段将同时显示一个 master:M 标记和一个 shared:N 标记。

继续停在新命名空间中,我们在 /mntX 和 /mntY 下创建挂载:

代码语言:txt
复制
sh2# mkdir /mntX/a
sh2# mount /dev/sda3 /mntX/a
sh2# mkdir /mntY/b
sh2# mount /dev/sda5 /mntY/b

当我们检查新挂载命名空间中挂载点的状态时,我们看到 /mntX/a 被创建为新的共享挂载(从其父挂载继承“共享”设置),/mntY/b 被创建为私有挂载(即,在可选字段中没有显示标记):

代码语言:txt
复制
sh2# cat /proc/self/mountinfo | grep '/mnt' | sed 's/ - .*//'
168 167 8:23 / /mntX rw,relatime shared:1
169 167 8:22 / /mntY rw,relatime master:2
173 168 8:3 / /mntX/a rw,relatime shared:3
175 169 8:5 / /mntY/b rw,relatime

回到第一个终端,我们看到 mount/mntX/a 传播了到最初命名空间中的对等方 /mntX ,但是 mount/mntY/b 没有传播:

代码语言:txt
复制
sh1# cat /proc/self/mountinfo | grep '/mnt' | sed 's/ - .*//'
132 83 8:23 / /mntX rw,relatime shared:1
133 83 8:22 / /mntY rw,relatime shared:2
174 132 8:3 / /mntX/a rw,relatime shared:3

接下来,我们在最初挂载命名空间中的 /mntY 下创建一个新的挂载点:

代码语言:txt
复制
sh1# mkdir /mntY/c
sh1# mount /dev/sda1 /mntY/c
sh1# cat /proc/self/mountinfo | grep '/mnt' | sed 's/ - .*//'
132 83 8:23 / /mntX rw,relatime shared:1
133 83 8:22 / /mntY rw,relatime shared:2
174 132 8:3 / /mntX/a rw,relatime shared:3
178 133 8:1 / /mntY/c rw,relatime shared:4

当我们检查第二个挂载命名空间中的挂载点时,看到在这种情况下,新挂载已传播到从属挂载点,并且新挂载是从属挂载(到对等组4):

代码语言:txt
复制
sh2# cat /proc/self/mountinfo | grep '/mnt' | sed 's/ - .*//'
168 167 8:23 / /mntX rw,relatime shared:1
169 167 8:22 / /mntY rw,relatime master:2
173 168 8:3 / /mntX/a rw,relatime shared:3
175 169 8:5 / /mntY/b rw,relatime
179 169 8:1 / /mntY/c rw,relatime master:4

绑定挂载

稍后,我们将讨论 MS_UNBINDABLE 传播类型的使用。但是,先简要对绑定挂载的概念进行描述是很有用的,这是 Linux 2.4 中首次出现的特性。

绑定挂载可使得文件或目录子树在单个目录层次结构中的其他位置可见。在某些方面,绑定挂载类似于硬链接,但又在一些重要方面有所不同:

  • 无法创建目录的硬链接,但可以将挂载绑定到目录。
  • 只能对同一个文件系统上的文件建立硬链接,而绑定挂载可以跨越文件系统(甚至可以从 chroot() 限制环境中获得)。
  • 硬链接需要修改文件系统,相比之下,绑定挂载是挂载命名空间的挂载列表中的记录,换句话说,是运行中的系统的属性。

可以通过带 MS_BIND 标志的 mount() 创建绑定挂载,也可以在命令行上使用 mount--bind 创建绑定装载。在下面的示例中,我们首先创建一个包含文件的目录,然后在新位置对该目录进行绑定挂载:

代码语言:txt
复制
# mkdir dir1                 # Create source directory
# touch dir1/x               # Populate the directory
# mkdir dir2                 # Create target for bind mount
# mount --bind dir1 dir2     # Create bind mount
# ls dir2                    # Bind mount has same content
x

然后,我们在新挂载点下创建一个文件,并观察到该新文件在原始目录下也是可见的,这表明绑定挂载引用了同一个目录对象:

代码语言:txt
复制
# touch dir2/y
# ls dir1
x  y

默认情况下,在创建目录的绑定挂载时,只有该目录会被挂载到新位置;该目录树下的任何挂载都不会被复制到挂载目标。可以通过使用 带 MS_BIND 和 MS_REC 标志的 mount(),或者在命令行中使用 mount--rbind 选项,递归地绑定挂载。在这种情况下,源树下的每个挂载都将复制到目标树中的相应位置。

MS_UNBINDABLE 示例

共享、私有和从属传播类型是用来管理对等挂载点(通常位于不同命名空间中)之间挂载事件的传播的。不可挂载点用来解决不同的问题,即挂载命名空间出现前的问题。这个问题就是所谓的“挂载点爆炸”,当在低级别挂载点重复执行高级别子树的递归绑定挂载时发生。我们通过一个 shell 会话演示该问题,然后看看不可绑定的挂载是如何解决该问题的。

首先,假设我们有一个有两个挂载点的系统,如下所示:

代码语言:txt
复制
# mount | awk '{print $1, $2, $3}'
/dev/sda1 on /
/dev/sdb6 on /mntX

现在假设我们要递归地将根目录绑定挂载到几个用户主目录下。我们将为第一个用户执行此操作并检查挂装点。首先创建一个新的命名空间,在该命名空间中,我们递归地将所有挂载点标记为从属,以防止对其它挂载命名空间产生副作用:

代码语言:txt
复制
# unshare -m sh
# mount --make-rslave /
# mount --rbind / /home/cecilia
# mount | awk '{print $1, $2, $3}'
/dev/sda1 on /
/dev/sdb6 on /mntX
/dev/sda1 on /home/cecilia
/dev/sdb6 on /home/cecilia/mntX

当我们对第二个用户重复递归绑定操作时,可看到爆炸问题:

代码语言:txt
复制
# mount --rbind / /home/henry
# mount | awk '{print $1, $2, $3}'
/dev/sda1 on /
/dev/sdb6 on /mntX
/dev/sda1 on /home/cecilia
/dev/sdb6 on /home/cecilia/mntX
/dev/sda1 on /home/henry
/dev/sdb6 on /home/henry/mntX
/dev/sda1 on /home/henry/home/cecilia
/dev/sdb6 on /home/henry/home/cecilia/mntX

在 /home/henry 下,我们不仅递归地添加了/mntX 挂载,还添加了在上一步中创建的 /home/cecilia 的递归挂载。在为第三个用户重复该步骤并简单地计算挂载量后,很明显,爆炸是指数级的:

代码语言:txt
复制
# mount --rbind / /home/otto
# mount | awk '{print $1, $2, $3}' | wc -l
16

通过使每个新的挂载不可绑定来避免该绑定爆炸问题。这样做的效果是,根目录的递归绑定挂载不会复制不可绑定挂载。回到最初的场景,我们为第一个用户创建一个不可绑定挂载,并通过 /proc/self/mountinfo 检查挂载:

代码语言:txt
复制
# mount --rbind --make-unbindable / /home/cecilia
# cat /proc/self/mountinfo | grep /home/cecilia | sed 's/ - .*//' 
108 83 8:2 / /home/cecilia rw,relatime unbindable
...

在 /proc/self/mountinfo 记录的可选字段中,显示了一个带有不可绑定标记的不可绑定挂载。

现在,我们为其他两个用户创建 unbindable 递归绑定挂载:

代码语言:txt
复制
# mount --rbind --make-unbindable / /home/henry
# mount --rbind --make-unbindable / /home/otto

在检查挂载点列表时,我们看到没有挂载点爆炸,因为不可绑定挂载没有被复制到用户的目录下:

代码语言:txt
复制
# mount | awk '{print $1, $2, $3}'
/dev/sda1 on /
/dev/sdb6 on /mntX
/dev/sda1 on /home/cecilia
/dev/sdb6 on /home/cecilia/mntX
/dev/sda1 on /home/henry
/dev/sdb6 on /home/henry/mntX
/dev/sda1 on /home/otto
/dev/sdb6 on /home/otto/mntX

结束语

挂载命名空间与共享子树特征结合,是创建每-用户和每-容器文件系统树的强大而灵活的工具。它们也是一个令人惊讶的复杂特性,我们已经试图在本文中解开一些复杂点。然而,实际上还有几个问题没有考虑。例如,有详细的规则描述在执行绑定挂载和移动(mount--move)操作时所得到的传播类型,也有规则描述改变挂载的传播类型时的结果。其中许多细节可以在内核源文件Documentation/filesystems/sharedsubtree.txt 中找到。


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

公众号:Geek乐园

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

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

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

本文系外文翻译前往查看

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

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