linux mount模式解析

参考资料
Linux Bind Mount 和 Mount Propagation——苦逼程序猿BOB

Mount namespaces, mount propagation, and unbindable mounts
——linux官方文档

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单次增长。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值