参考资料
Linux Bind Mount 和 Mount Propagation——苦逼程序猿BOB
Mount namespaces, mount propagation, and unbindable mounts
——linux官方文档
mount和umount解析
linux mount
mount结构
本节内容转载自linux内核mount过程超复杂的do_mount()、do_loopback()、attach_recursive_mnt()、propagate_mnt()函数详解
struct mount {
/*mount靠mnt_hash链入mount hash链表,__lookup_mnt()是从该mount hash链表
搜索mount结构。commit_tree()和attach_mnt()中靠mnt_hash把mount链入mount hash
链表,并且链入hash表的键值是(父mount结构的vfsmount成员+该mount的挂载点dentry)*/
struct list_head mnt_hash;
/*父mount,attach_recursive_mnt->mnt_set_mountpoint(),竟然设置为挂点目录所
在文件系统的mount,也说也是,挂载源的mount的父mount是挂载点目录所在的文件系统的mount结构*/
struct mount *mnt_parent;
/*挂载点dentry,attach_recursive_mnt->mnt_set_mountpoint()设置为挂载点目录dentry*/
struct dentry *mnt_mountpoint;
//包含块设备的根目录dentry,每个mount结构对应一个唯一的vfsmount。
struct vfsmount mnt;
/*commit_tree()靠mnt_child把mount结构添加到mount的parent mount的
mnt_mounts链表,所以这个看着是mount的子mount结构保存的链表*/
struct list_head mnt_mounts;
/*next_mnt()里根据mnt_child返回其mount结构,commit_tree()和attach_mnt()靠
mnt_child把mount结构添加到mount的mnt_parent的mnt_mounts链表*/
struct list_head mnt_child;
//copy_tree()创建的新mount并靠mnt_list添加到该链表,搞不懂有什么用?
struct list_head mnt_list;
//clone_mnt()把本次挂载的source mount通过其mnt_share链接到克隆母体的mnt_share链表
struct list_head mnt_share;
/*clone_mnt()中,把本次挂载slave属性的source mount结构链接到克隆母体mount
的mnt_slave_list链表。mount结构的mnt_slave_list链表是保存子slave mount的,凡
是照着一个mount结构克隆生成的mount,都添加到克隆母体的mnt_slave_list链表,克
隆的mount是母体子slave mount*/
struct list_head mnt_slave_list;
/* 1 clone_mnt()中,把本次挂载source slave属性的mount结构链接到克隆母体mount
的mnt_slave_list链表2 clone_mnt()中,克隆母体是slave属性而本次source mount没
有指定属性,则source mount被添加到与克隆母体同一个mount salve组链表具体添加形
式是,source mount结构靠其mnt_slave添加到克隆母体的mnt_slave链表。source mount
和克隆母体靠各自的mnt_slave构成链表,二者是同一个mount slave组成员。如果source
mount靠其mnt_slave添加到克隆母体的mnt_slave_list链表,则二者是父子关系,不是同组关系。*/
struct list_head mnt_slave;
/* 1 clone_mnt()中,本次挂载是slave属性,克隆生成的source mount,即mnt,其
mnt_master指向克隆母体的mount结构。2 clone_mnt()中,本次挂载没有指定mount属性,
而克隆母体又是slave属性,则souece mount的mnt_master就是克隆母体的
mount->mnt_master,二者属于同一个mount slave组。3 正常mount /dev/sda3 /home这
样生成的mount其mnt_master是NULL,mount bind的share属性的mount其mnt_master是NULL*/
struct mount *mnt_master;
//mount所属命名空间,commit_tree()中把mount结构添加到父mount的mnt_ns的list链表
struct mnt_namespace *mnt_ns;
//挂载点结构,包含挂载点dentry,attach_recursive_mnt->mnt_set_mountpoint()中设置
struct mountpoint *mnt_mp;
//mount group id,一个mount组里,所有的mount结构的mnt_group_id一样.就是靠这个判断两个mount是否属于同一个peer group
int mnt_group_id;
};
1 每一次mount挂载块设备或者tmpfs都要生成一个mount结构体。mount结构每次mount挂载都生成一个,作为本次挂载的source mount,dest mount是挂载点目录的所在文件系统的mount结构。source mount和dest mount是相对的,现在的source mount说不定下次就成了dest mount。比如”mount -t ext4 /dev/sda1 /” 挂载到根目录,假设本次挂载sda1的source mount结构是 mount1,可以理解成根文件系统对应的mount结构是mount1。接着”mount -t ext4 /dev/sda3 /home/”, 假设本次挂载sda3生成source mount是mount2,由于挂载点目录”/home”是根文件系统下的目录,则dest mount是上次的source mount,即mount1。如果再接着有”mount -t ext4 /dev/sda5 /home/test”, 假设本次挂载sda5的source mount结构是 mount3,由于挂载点目录”/home/test”是sda3 ext4文件系统下的”test”目录,所以本次的dest mount是上次的source mount,即mount2。
2 mount结构的成员struct mount *mnt_parent指向其父mount结构,父子mount该怎么定义呢?就是本次的挂载的source mount和dest mount构成父子关系,即source mount->mnt_parent=dest mount。
查看mount点
通过命令 cat /proc/pid/mountinfo可以查看任意pid的进程的挂载点。特别的,通过cat /proc/self/mountinfo可以查看当前进程的挂载点。也可以使用findmnt
[root@v5 ~]# cat /proc/self/mountinfo
source_mnt_id dest_mnt_id source_dir dest_dir options mnt_peer_group 文件系统 options
17 38 0:16 / /sys rw,nosuid,nodev,noexec,relatime shared:6 - sysfs sysfs rw,seclabel
[root@v5 ~]# cat /proc/self/mountinfo
17 38 0:16 / /sys rw,nosuid,nodev,noexec,relatime shared:6 - sysfs sysfs rw,seclabel
18 38 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw
19 38 0:5 / /dev rw,nosuid shared:2 - devtmpfs devtmpfs rw,seclabel,size=1929988k,nr_inodes=482497,mode=755
20 17 0:15 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:7 - securityfs securityfs rw
21 19 0:17 / /dev/shm rw,nosuid,nodev,noexec,relatime shared:3 - tmpfs tmpfs rw,seclabel
22 19 0:11 / /dev/pts rw,nosuid,noexec,relatime shared:4 - devpts devpts rw,seclabel,gid=5,mode=620,ptmxmode=000
23 38 0:18 / /run rw,nosuid,nodev shared:23 - tmpfs tmpfs rw,seclabel,mode=755
24 17 0:19 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:8 - tmpfs tmpfs ro,seclabel,mode=755
25 24 0:20 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:9 - cgroup cgroup rw,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd
26 17 0:21 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime shared:20 - pstore pstore rw
27 24 0:22 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:10 - cgroup cgroup rw,perf_event
28 24 0:23 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:11 - cgroup cgroup rw,memory
29 24 0:24 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:12 - cgroup cgroup rw,cpuacct,cpu
30 24 0:25 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:13 - cgroup cgroup rw,blkio
31 24 0:26 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,pids
32 24 0:27 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,cpuset
33 24 0:28 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,devices
34 24 0:29 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,net_prio,net_cls
35 24 0:30 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,freezer
36 24 0:31 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,hugetlb
37 17 0:32 / /sys/kernel/config rw,relatime shared:21 - configfs configfs rw
38 1 253:0 / / rw,relatime shared:1 - xfs /dev/mapper/ncl-root rw,seclabel,attr2,inode64,noquota
39 17 0:14 / /sys/fs/selinux rw,relatime shared:22 - selinuxfs selinuxfs rw
40 18 0:33 / /proc/sys/fs/binfmt_misc rw,relatime shared:24 - autofs systemd-1 rw,fd=30,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=12631
41 19 0:13 / /dev/mqueue rw,relatime shared:25 - mqueue mqueue rw,seclabel
42 19 0:34 / /dev/hugepages rw,relatime shared:26 - hugetlbfs hugetlbfs rw,seclabel
43 17 0:6 / /sys/kernel/debug rw,relatime shared:27 - debugfs debugfs rw
44 38 0:35 / /tmp rw,nosuid,nodev,noexec shared:28 - tmpfs tmpfs rw,seclabel
45 38 8:1 / /boot rw,relatime shared:29 - xfs /dev/sda1 rw,seclabel,attr2,inode64,noquota
47 23 0:37 / /run/user/0 rw,nosuid,nodev,relatime shared:30 - tmpfs tmpfs rw,seclabel,size=388124k,mode=700
[root@v5 ~]# findmnt
TARGET SOURCE FSTYPE OPTIONS
/ /dev/mapper/ncl-root
xfs rw,relatime,seclabel,attr2,inode64,
├─/sys sysfs sysfs rw,nosuid,nodev,noexec,relatime,sec
│ ├─/sys/kernel/security securityfs securityf rw,nosuid,nodev,noexec,relatime
│ ├─/sys/fs/cgroup tmpfs tmpfs ro,nosuid,nodev,noexec,seclabel,mod
│ │ ├─/sys/fs/cgroup/systemd cgroup cgroup rw,nosuid,nodev,noexec,relatime,xat
│ │ ├─/sys/fs/cgroup/perf_event cgroup cgroup rw,nosuid,nodev,noexec,relatime,per
│ │ ├─/sys/fs/cgroup/memory cgroup cgroup rw,nosuid,nodev,noexec,relatime,mem
│ │ ├─/sys/fs/cgroup/cpu,cpuacct cgroup cgroup rw,nosuid,nodev,noexec,relatime,cpu
│ │ ├─/sys/fs/cgroup/blkio cgroup cgroup rw,nosuid,nodev,noexec,relatime,blk
│ │ ├─/sys/fs/cgroup/pids cgroup cgroup rw,nosuid,nodev,noexec,relatime,pid
│ │ ├─/sys/fs/cgroup/cpuset cgroup cgroup rw,nosuid,nodev,noexec,relatime,cpu
│ │ ├─/sys/fs/cgroup/devices cgroup cgroup rw,nosuid,nodev,noexec,relatime,dev
│ │ ├─/sys/fs/cgroup/net_cls,net_prio cgroup cgroup rw,nosuid,nodev,noexec,relatime,net
│ │ ├─/sys/fs/cgroup/freezer cgroup cgroup rw,nosuid,nodev,noexec,relatime,fre
│ │ └─/sys/fs/cgroup/hugetlb cgroup cgroup rw,nosuid,nodev,noexec,relatime,hug
│ ├─/sys/fs/pstore pstore pstore rw,nosuid,nodev,noexec,relatime
│ ├─/sys/kernel/config configfs configfs rw,relatime
│ ├─/sys/fs/selinux selinuxfs selinuxfs rw,relatime
│ └─/sys/kernel/debug debugfs debugfs rw,relatime
├─/proc proc proc rw,nosuid,nodev,noexec,relatime
│ └─/proc/sys/fs/binfmt_misc systemd-1 autofs rw,relatime,fd=30,pgrp=1,timeout=0,
├─/dev devtmpfs devtmpfs rw,nosuid,seclabel,size=1929988k,nr
│ ├─/dev/shm tmpfs tmpfs rw,nosuid,nodev,noexec,relatime,sec
│ ├─/dev/pts devpts devpts rw,nosuid,noexec,relatime,seclabel,
│ ├─/dev/mqueue mqueue mqueue rw,relatime,seclabel
│ └─/dev/hugepages hugetlbfs hugetlbfs rw,relatime,seclabel
├─/run tmpfs tmpfs rw,nosuid,nodev,seclabel,mode=755
│ └─/run/user/0 tmpfs tmpfs rw,nosuid,nodev,relatime,seclabel,s
├─/tmp tmpfs tmpfs rw,nosuid,nodev,noexec,seclabel
└─/boot /dev/sda1 xfs rw,relatime,seclabel,attr2,inode64,
首先,内核挂载根文件系统,本文不讲根文件系统的挂载。
根文件系统挂载完毕以后,开始挂载其他目录,拿/run和/run/user/0举例:
38 1 253:0 / / rw,relatime shared:1 - xfs /dev/mapper/ncl-root rw,seclabel,attr2,inode64,noquota
23 38 0:18 / /run rw,nosuid,nodev shared:23 - tmpfs tmpfs rw,seclabel,mode=755
47 23 0:37 / /run/user/0 rw,nosuid,nodev,relatime shared:30 - tmpfs tmpfs rw,seclabel,size=388124k,mode=700
具体挂载流程如下:
mount -t tmpfs tmpfs /run
23 38 0:18 / /run rw,nosuid,nodev shared:23 - tmpfs tmpfs rw,seclabel,mode=755
tmpfs文件系统会从内存上划出一块空间,作为该文件系统的存储位置。每一个tmpfs在内存上都是一块新的区域。
该命令的意思是,创建一个tmpfs文件系统,将该文件系统的根目录,挂载到主机根目录的/run目录下。其意思是mount -t tmpfs tmpfs / /run
挂载源是tmpfs文件系统的“/”,挂载点目录则是主机xfs文件系统中的“/run”。
通俗点说是"tmpfs/"->“xfs/run”。
流程如下:
1、以挂载源“/”生成一个source mount结构体,生成mount id,为23
2、将挂载点目录“/run”的父目录挂载时生成的source mount作为本次挂载的dest mount结构体,生成dest mount id。挂载点目录“/run”的父目录是“/”,xfs文件系统,该文件系统的mount事件为“/ / xfs”,mount id是38。将source mount id为38的mount结构体作为本次mount的dest mount结构体。本次挂载的23 38
生成。
3、查找本次tmpfs根文件系统是否在主机上存在挂载点,由于每个tmpfs文件系统是内存上单独的一块区域,互相独立,所以本次tmpfs根文件系统在主机上不存在其他挂载点,因此,创建新的mount peer group,本次生成的group id为23,由于mount未指定propagation参数,默认为shared模式,即shared:23
mount -t tmpfs tmpfs -o rw,seclabel,size=388124k,mode=700 /run/user/0
47 23 0:37 / /run/user/0 rw,nosuid,nodev,relatime shared:30 - tmpfs tmpfs rw,seclabel,size=388124k,mode=700
-o
表示挂载参数,这里不讨论-o
参数的意义。
再来一遍,创建一个tmpfs文件系统,将该文件系统的根目录,挂载到主机根目录的/run/user/0目录下。其意思是mount -t tmpfs tmpfs / /run/user/0
挂载源是tmpfs文件系统的“/”,挂载点目录则是主机xfs文件系统中的“/run/user/0”。
通俗点说是"tmpfs/"->“xfs/run/user/0”
流程如下:
1、以挂载源“/”生成一个source mount结构体,生成mount id,为47
2、将挂载点目录“/run/user/0”的父目录挂载时生成的source mount作为本次挂载的dest mount结构体,生成dest mount id。
注意,这里挂载点目录“/run/user/0”的父目录是“/run/user”,没有挂载点,在继续找“/run/user”的父目录,“/run/user”的父目录“/run”目录已经挂载了,此时的“/run”是上一次“/run”挂载时的“tmpfs/”。所以“/run/user/0”所在文件系统是“tmpfs/”,tmpfs文件系统,该文件系统的mount事件为“/ /run tmpfs tmpfs”,mount id是23。将source mount id为23的mount结构体作为本次mount的dest mount结构体。本次挂载的47 23
生成。
3、查找本次tmpfs根文件系统是否在主机上存在挂载点,由于每个tmpfs文件系统是内存上单独的一块区域,互相独立,与“/run”挂载时的“tmpfs/”是不同的内存区域,所以本次tmpfs根文件系统在主机上不存在其他挂载点,因此,创建新的mount peer group,本次生成的group id为30,由于mount未指定propagation参数,默认为shared模式,即shared:30
再举一个例子巩固一下
创建两个文件夹,source_dir和dest_dir
[root@v5 ~]# mkdir -p mount-test/source_dir mount-test/dest_dir
完整目录为/root/mount-test/source_dir和/root/mount-test/dest_dir
将source_dir挂载到dest_dir上。
[root@v5 ~]# mount --rbind /root/mount-test/source_dir/ /root/mount-test/dest_dir/
[root@v5 ~]# cat /proc/self/mountinfo | grep mount-test
48 38 253:0 /root/mount-test/source_dir /root/mount-test/dest_dir rw,relatime shared:1 - xfs /dev/mapper/ncl-root rw,seclabel,attr2,inode64,noquota
再来分析一遍
1、以挂载源“/root/mount-test/source_dir”生成一个source mount结构体,生成mount id,为48
2、将挂载点目录“/root/mount-test/source_dir”的父目录挂载时生成的source mount作为本次挂载的dest mount结构体,生成dest mount id。
注意,这里挂载点目录“/root/mount-test/dest”的父目录是“/root/mount-test”,没有挂载点,在继续找“/root”的父目录,也没有挂载点,再继续找“/root”的父目录,“/root”的父目录为“/”,存在挂载点。“/root/mount-test/dest”所在文件系统是“xfs/”,xfs文件系统,该文件系统的mount事件为“/ / xfs”,mount id是38。将source mount id为38的mount结构体作为本次mount的dest mount结构体。本次挂载的47 38
生成。
3、查找本次挂载源“/root/mount-test/source_dir”的父目录“/root/mount-test”,没有挂载点,继续查找父,“/root”,没有父,继续查找父“/”。父“/”挂载时生成的mount peer组为shared:1,将本次挂载加入到该组中,本次生成的group id为1,由于mount未指定propagation参数,默认为shared模式,即shared:1
bash-1
|
|----sh shared模式 该进程执行mount,指数增长((2^(n-1) + 1)*m + bash-mount数),且会传递给shared/slave模式的子进程。
|+++|
|+++|----sh shared模式 该进程执行mount,指数增长,且会传递给shared/slave模式的子进程,也会传递给shared模式的父进程,且该进程退出,父进程的mount数量不会减少。
|+++|
|+++|----sh 不指定模式 该进程mount模式继承自父进程
|
|----sh slave模式 该进程执行mount,mount单次增长。不会传递给父进程。会传递给shared/slave模式的子进程,子进程执行mount,该进程不会受到影响(mount不增加)
|
|----sh private模式 mount挂载点继承自父进程,该进程执行mount,不对其他任何进程产生影响,mount单次增长。