资讯专栏INFORMATION COLUMN

Linux mount (第二部分 - Shared subtrees)

zone / 1316人阅读

简单点说,Shared subtrees就是一种控制子挂载点能否在其他地方被看到的技术,它只会在bind mount和mount namespace中用到,属于不怎么常用的功能。本篇将以bind mount为例对Shared subtrees做一个简单介绍,

本篇所有例子都在ubuntu-server-x86_64 16.04下执行通过

概述

回想一下上一篇中介绍的bind mount部分,如果bind在一起的两个目录下的子目录再挂载了设备的话,他们之间还能相互看到子目录里挂载的内容吗? 比如在第一个目录下的子目录里面再mount了一个设备,那么在另一个目录下面能看到这个mount的设备里面的东西吗?答案是要看bind mount的propagation type。那什么是propagation type呢?

peer group和propagation type都是随着shared subtrees一起被引入的概念,下面分别对他们做一个介绍。

peer group

peer group就是一个或多个挂载点的集合,他们之间可以共享挂载信息。目前在下面两种情况下会使两个挂载点属于同一个peer group(前提条件是挂载点的propagation type是shared)

利用mount --bind命令,将会使源和目的挂载点属于同一个peer group,当然前提条件是‘源’必须要是一个挂载点。

当创建新的mount namespace时,新namespace会拷贝一份老namespace的挂载点信息,于是新的和老的namespace里面的相同挂载点就会属于同一个peer group。

propagation type

每个挂载点都有一个propagation type标志, 由它来决定当一个挂载点的下面创建和移除挂载点的时候,是否会传播到属于相同peer group的其他挂载点下去,也即同一个peer group里的其他的挂载点下面是不是也会创建和移除相应的挂载点.现在有4种不同类型的propagation type:

MS_SHARED: 从名字就可以看出,挂载信息会在同一个peer group的不同挂载点之间共享传播. 当一个挂载点下面添加或者删除挂载点的时候,同一个peer group里的其他挂载点下面也会挂载和卸载同样的挂载点

MS_PRIVATE: 跟上面的刚好相反,挂载信息根本就不共享,也即private的挂载点不会属于任何peer group

MS_SLAVE: 跟名字一样,信息的传播是单向的,在同一个peer group里面,master的挂载点下面发生变化的时候,slave的挂载点下面也跟着变化,但反之则不然,slave下发生变化的时候不会通知master,master不会发生变化。

MS_UNBINDABLE: 这个和MS_PRIVATE相同,只是这种类型的挂载点不能作为bind mount的源,主要用来防止递归嵌套情况的出现。这种类型不常见,本篇将不介绍这种类型,有兴趣的同学请参考这里的例子。

还有一些概念需要澄清一下:

propagation type是挂载点的属性,每个挂载点都是独立的

挂载点是有父子关系的,比如挂载点/和/mnt/cdrom,/mnt/cdrom都是‘/’的子挂载点,‘/’是/mnt/cdrom的父挂载点

默认情况下,如果父挂载点是MS_SHARED,那么子挂载点也是MS_SHARED的,否则子挂载点将会是MS_PRIVATE,跟爷爷挂载点没有关系

示例

这里将只演示bind mount的情况,mount namespace的情况请参考这里

准备环境
#准备4个虚拟的disk,并在上面创建ext2文件系统,用于后续的mount测试
dev@ubuntu:~$ mkdir disks && cd disks
dev@ubuntu:~/disks$ dd if=/dev/zero bs=1M count=32 of=./disk1.img
dev@ubuntu:~/disks$ dd if=/dev/zero bs=1M count=32 of=./disk2.img
dev@ubuntu:~/disks$ dd if=/dev/zero bs=1M count=32 of=./disk3.img
dev@ubuntu:~/disks$ dd if=/dev/zero bs=1M count=32 of=./disk4.img
dev@ubuntu:~/disks$ mkfs.ext2 ./disk1.img
dev@ubuntu:~/disks$ mkfs.ext2 ./disk2.img
dev@ubuntu:~/disks$ mkfs.ext2 ./disk3.img
dev@ubuntu:~/disks$ mkfs.ext2 ./disk4.img
#准备两个目录用于挂载上面创建的disk
dev@ubuntu:~/disks$ mkdir disk1 disk2
dev@ubuntu:~/disks$ ls
disk1  disk1.img  disk2  disk2.img  disk3.img  disk4.img

#确保根目录的propagation type是shared,
#这一步是为了保证大家的操作结果和示例中的一样
dev@ubuntu:~/disks$ sudo mount --make-shared /
查看propagation type和peer group

默认情况下,子挂载点会继承父挂载点的propagation type

#显式的以shared方式挂载disk1
dev@ubuntu:~/disks$ sudo mount --make-shared ./disk1.img ./disk1
#显式的以private方式挂载disk2
dev@ubuntu:~/disks$ sudo mount --make-private ./disk2.img ./disk2

#mountinfo比mounts文件包含有更多的关于挂载点的信息
#这里sed主要用来过滤掉跟当前主题无关的信息
#shared:105表示挂载点/home/dev/disks/disk1是以shared方式挂载,且peer group id为105
#而挂载点/home/dev/disks/disk2没有相关信息,表示是以private方式挂载
dev@ubuntu:~/disks$ cat /proc/self/mountinfo |grep disk | sed "s/ - .*//"
164 24 7:1 / /home/dev/disks/disk1 rw,relatime shared:105
173 24 7:2 / /home/dev/disks/disk2 rw,relatime

#分别在disk1和disk2目录下创建目录disk3和disk4,然后挂载disk3,disk4到这两个目录
dev@ubuntu:~/disks$ sudo mkdir ./disk1/disk3 ./disk2/disk4
dev@ubuntu:~/disks$ sudo mount ./disk3.img ./disk1/disk3
dev@ubuntu:~/disks$ sudo mount ./disk4.img ./disk2/disk4

#查看挂载信息,第一列的数字是挂载点ID,第二例是父挂载点ID,
#从结果来看,176和164的类型都是shared,而179和173的类型都是private的,
#说明在默认mount的情况下,子挂载点会继承父挂载点的propagation type
dev@ubuntu:~/disks$ cat /proc/self/mountinfo |grep disk| sed "s/ - .*//"
164 24 7:1 / /home/dev/disks/disk1 rw,relatime shared:105 
173 24 7:2 / /home/dev/disks/disk2 rw,relatime 
176 164 7:3 / /home/dev/disks/disk1/disk3 rw,relatime shared:107 
179 173 7:4 / /home/dev/disks/disk2/disk4 rw,relatime 
shared 和 private mount
#umount掉disk3和disk4,创建两个新的目录bind1和bind2用于bind测试
dev@ubuntu:~/disks$ sudo umount /home/dev/disks/disk1/disk3
dev@ubuntu:~/disks$ sudo umount /home/dev/disks/disk2/disk4
dev@ubuntu:~/disks$ mkdir bind1 bind2

#bind的方式挂载disk1到bind1,disk2到bind2
dev@ubuntu:~/disks$ sudo mount --bind ./disk1 ./bind1
dev@ubuntu:~/disks$ sudo mount --bind ./disk2 ./bind2

#查看挂载信息,显然默认情况下bind1和bind2的propagation type继承自父挂载点24(/),都是shared。
#由于bind2的源挂载点disk2是private的,所以bind2没有和disk2在同一个peer group里面,
#而是重新创建了一个新的peer group,这个group里面就只有它一个。
#因为164和176都是shared类型且是通过bind方式mount在一起的,所以他们属于同一个peer group 105。
dev@ubuntu:~/disks$ cat /proc/self/mountinfo |grep disk| sed "s/ - .*//"
164 24 7:1 / /home/dev/disks/disk1 rw,relatime shared:105 
173 24 7:2 / /home/dev/disks/disk2 rw,relatime 
176 24 7:1 / /home/dev/disks/bind1 rw,relatime shared:105 
179 24 7:2 / /home/dev/disks/bind2 rw,relatime shared:109 

#ID为24的挂载点为根目录的挂载点
dev@ubuntu:~/disks$ cat /proc/self/mountinfo |grep ^24| sed "s/ - .*//"
24 0 252:0 / / rw,relatime shared:1

#这时disk3和disk4目录都是空的
dev@ubuntu:~/disks$ ls bind1/disk3/
dev@ubuntu:~/disks$ ls bind2/disk4/
dev@ubuntu:~/disks$ ls disk1/disk3/
dev@ubuntu:~/disks$ ls disk2/disk4/

#重新挂载disk3和disk4
dev@ubuntu:~/disks$ sudo mount ./disk3.img ./disk1/disk3
dev@ubuntu:~/disks$ sudo mount ./disk4.img ./disk2/disk4

#由于disk1/和bind1/属于同一个peer group,
#所以在挂载了disk3后,在两个目录下都能看到disk3下的内容
dev@ubuntu:~/disks$ ls disk1/disk3/
lost+found
dev@ubuntu:~/disks$ ls bind1/disk3/
lost+found

#而disk2/是private类型的,所以在他下面挂载disk4不会通知bind2,
#于是bind2下的disk4目录是空的
dev@ubuntu:~/disks$ ls disk2/disk4/
lost+found
dev@ubuntu:~/disks$ ls bind2/disk4/
dev@ubuntu:~/disks$

#再看看disk3,虽然182和183的父挂载点不一样,但由于他们父挂载点属于同一个peer group,
#且disk3是以默认方式挂载的,所以他们属于同一个peer group
dev@ubuntu:~/disks$ cat /proc/self/mountinfo |egrep "disk3"| sed "s/ - .*//"
182 164 7:3 / /home/dev/disks/disk1/disk3 rw,relatime shared:111 
183 176 7:3 / /home/dev/disks/bind1/disk3 rw,relatime shared:111 

#umount bind1/disk3后,disk1/disk3也相应的自动umount掉了
dev@ubuntu:~/disks$ sudo umount bind1/disk3
dev@ubuntu:~/disks$ cat /proc/self/mountinfo |grep disk3
dev@ubuntu:~/disks$
slave mount
#umount除disk1的所有其他挂载点
dev@ubuntu:~/disks$ sudo umount ./disk2/disk4
dev@ubuntu:~/disks$ sudo umount /home/dev/disks/bind1
dev@ubuntu:~/disks$ sudo umount /home/dev/disks/bind2
dev@ubuntu:~/disks$ sudo umount /home/dev/disks/disk2
#确认只剩disk1
dev@ubuntu:~/disks$ cat /proc/self/mountinfo |grep disk| sed "s/ - .*//"
164 24 7:1 / /home/dev/disks/disk1 rw,relatime shared:105 

#分别显式的用shared和slave的方式bind disk1
dev@ubuntu:~/disks$ sudo mount --bind --make-shared ./disk1 ./bind1
dev@ubuntu:~/disks$ sudo mount --bind --make-slave ./bind1 ./bind2

#164、173和176都属于同一个peer group,
#master:105表示/home/dev/disks/bind2是peer group 105的slave
dev@ubuntu:~/disks$ cat /proc/self/mountinfo |grep disk| sed "s/ - .*//"
164 24 7:1 / /home/dev/disks/disk1 rw,relatime shared:105 
173 24 7:1 / /home/dev/disks/bind1 rw,relatime shared:105 
176 24 7:1 / /home/dev/disks/bind2 rw,relatime master:105 

#mount disk3到disk1的子目录disk3下
dev@ubuntu:~/disks$ sudo mount ./disk3.img ./disk1/disk3/
#其他两个目录bin1和bind2里面也挂载成功,说明master发生变化的时候,slave会跟着变化
dev@ubuntu:~/disks$ cat /proc/self/mountinfo |grep disk| sed "s/ - .*//"
164 24 7:1 / /home/dev/disks/disk1 rw,relatime shared:105 
173 24 7:1 / /home/dev/disks/bind1 rw,relatime shared:105 
176 24 7:1 / /home/dev/disks/bind2 rw,relatime master:105 
179 164 7:2 / /home/dev/disks/disk1/disk3 rw,relatime shared:109 
181 176 7:2 / /home/dev/disks/bind2/disk3 rw,relatime master:109 
180 173 7:2 / /home/dev/disks/bind1/disk3 rw,relatime shared:109 

#umount disk3,然后mount disk3到bind2目录下
dev@ubuntu:~/disks$ sudo umount ./disk1/disk3/
dev@ubuntu:~/disks$ sudo mount ./disk3.img ./bind2/disk3/

#由于bind2的propagation type是slave,所以disk1和bind1两个挂载点下面不会挂载disk3
#从179的类型可以看出,当父挂载点176是slave类型时,默认情况下其子挂载点179是private类型
dev@ubuntu:~/disks$ cat /proc/self/mountinfo |grep disk| sed "s/ - .*//"
164 24 7:1 / /home/dev/disks/disk1 rw,relatime shared:105 
173 24 7:1 / /home/dev/disks/bind1 rw,relatime shared:105 
176 24 7:1 / /home/dev/disks/bind2 rw,relatime master:105 
179 176 7:2 / /home/dev/disks/bind2/disk3 rw,relatime -
结束语

如果用到了bind mount和mount namespace,在挂载设备的时候就需要注意一下父挂载点是否和其他挂载点有peer group关系,如果有且父挂载点是shared,就说明你挂载的设备除了在当前挂载点可以看到,在父挂载点的peer group的下面也可以看到。

参考

kernel:Shared Subtrees

lwn:Shared subtrees

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/9460.html

相关文章

  • Linux Namespace系列(04):mount namespaces (CLONE_NEWN

    摘要:是第一个被加入的,由于当时没想到还会引入其它的,所以取名为,而没有叫。 Mount namespace用来隔离文件系统的挂载点, 使得不同的mount namespace拥有自己独立的挂载点信息,不同的namespace之间不会相互影响,这对于构建用户或者容器自己的文件系统目录非常有用。 当前进程所在mount namespace里的所有挂载信息可以在/proc/[pid]/mount...

    shadowbook 评论0 收藏0
  • Linux mount一部

    摘要:大部分情况下都不用指定该参数,系统都会自动检测到设备上的文件系统类型挂载参数可选。由于没有对应的源设备,这里可以是任意字符串,取个有意义的名字就可以了,因为用命令查看挂载点信息时第一列显示的就是这个字符串。下一篇中将介绍相关的参考 本篇将介绍一些比较实用的mount用法,包括挂载内核中的虚拟文件系统、loop device和bind mount。 下面的所有例子都在ubuntu-ser...

    NeverSayNever 评论0 收藏1
  • Linux Namespace系列(08):user namespace (CLONE_NEWUSE

    摘要:本篇将主要介绍和其他类型的的关系。权限涉及的范围非常广,所以导致比其他的要复杂同时权限也是容器安全的基础,所以非常重要。其它和类似,当在程序中用将一个的或者发送给另一个中的进程时,内核会自动映射成目的中对应的或者。 本篇将主要介绍user namespace和其他类型的namespace的关系。 权限涉及的范围非常广,所以导致user namespace比其他的namespace要复杂...

    lsxiao 评论0 收藏0
  • 走进docker(04):什么是容器的runtime?

    摘要:容器的注释,相当于容器标签,来自于容器的配置文件,格式。执行命令启动容器执行中配置的钩子执行中指定的程序,这时容器状态变成了执行钩子。容器由于某些原因退出,比如容器中的第一个进程主动退出,挂掉或者被掉等。 我们都知道runc是容器runtime的一个实现,那到底什么是runtime?包含了哪些内容? 容器的runtime和image一样,也有标准,也由OCI (Open Contain...

    wangxinarhat 评论0 收藏0
  • Linux命令之:free命令

    摘要:占用的内存页,也就是物理内存不足。事实上,少量地使用是不是影响到系统性能的。 free命令可以显示当前系统未使用的和已使用的内存数目,还可以显示被内核使用的内存缓冲区 语法 free [选项] 选项 -b:以Byte为单位显示内存使用情况; -k:以KB为单位显示内存使用情况; -m:以MB为单位显示内存使用情况; -o:不显示缓冲区调节列; -s:持续观察内存使用状况; -...

    rockswang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<