1. 文件系统层次化

对 Linux 系统来说一切皆文件,Linux 使用树形的层次化结构来管理所有的文件对象。

完整的Linux文件系统,是由多种设备、多种文件系统组成的一个混合的树形结构。我们首先从一个单独的块设备来分析其树形结构的构造。

1.1 块设备的层次化(superblock/inode/dentry)

对一个块设备来说要想构造文件系统树形结构,最重要的两个全局因素是:块设备(block_device)和文件系统(file_system_type)。内核使用了一个数据结构 struct super_block 把这两者结合起来,用来标识一个块设备。

确定了 super_block 以后,就可以使用文件系统提供的方法来解析块设备的内容,形成一个块设备内部的树形结构,也就是我们熟悉的文件夹、文件的层次结构。

系统使用 struct inode 结构来标识块设备内部的一个文件夹或者文件,struct inode 结构中最重要的成员是 ->i_ino 这个记录了 inode 在块设备中的偏移。

系统为了辅助 struct inode 的使用还设计了一个 struct dentry 结构,通常情况下一个 struct dentry 对应一个 struct inode,也有少数情况下多个 struct dentry 对应一个 struct inode(如硬链接)。struct dentry 中 cache 了更多的文件信息,类如文件名、层次结构,成员 ->d_parent 指向同一块设备内的父节点 struct dentry ,成员 ->d_subdirs 链接了所有的子节点 struct dentry

1.2 多设备的层次化(mount/vfsmount)

上一节描述了单个块设备构建树形文件系统的方式,如果是多个设备怎么样组成一颗更复杂的树呢?

Linux使用父子树的形式来构造,父设备树中的一个文件夹 struct dentry 可以充当子设备树的挂载点 mountpoint

  • mount/vfsmount

为了组成复杂的父子树,系统定义了一个 struct mount 结构来负责对一个设备内子树的引用。

  • mount tree

可以看到通过一个 struct mount 结构负责引用一颗子设备树,把这颗子设备树挂载到父设备树的其中一个 dentry 节点上。

如果 dentry 成为了挂载点 mountpoint,会给其标识成 DCACHE_MOUNTED。我们在查找路径的时候同样会判断 dentryDCACHE_MOUNTED 标志,一旦置位就变成了 mountpoint,挂载点文件夹下原有的内容就不能访问了,转而访问子设备树根节点下的内容。

struct mount 结构之间也构成了树形结构。

我们通常使用 mount -t fstpye devname pathname 命令来进行挂载子设备的操作。Linux 拥有非常灵活的挂载规则:

规则1、一个设备可以被挂载多次:

可以看到同一个子设备树,同时被两个 struct mount 结构所引用,被挂载到父设备树的两处不同的 dentry 处。

特别说明:虽然子设备树被挂载两次并且通过两处路径都能访问,但子设备的 dentryinode 只保持一份。

规则2、一个挂载点可以挂载多个设备:

还可以对父设备树的同一个文件夹 dentry 进行多次挂载,最后路径查找时生效的是最后一次挂载的子设备树。

  • path

因为linux提供的灵活的挂载规则,所以我们如果要标识一个路径 struct path 的话需要两个元素:vfsmountdentry

可以看到两个路径 struct path 最后引用到了同一 inode,但是路径 path 是不一样的,因为 path 指向的 vfsmount 是不一样的。

  • chroot

Linux 还支持每个进程拥有不同的根目录,使用 chroot() 系统调用可以把当前进程的根目录设置为整棵文件系统树中的任何 path

1.3 多名空间的层次化(mnt_namespace)

之前的系统中只有一棵mount树,为了支持mnt_namespace,系统把mount树叶扩展成了多棵。每个mnt_namespace拥有一棵独立的mount树:

2. 关键代码

2.1 mount()

mount() 系统调用是理解文件系统层次化的核心,它主要包含3个关键步骤:

1、解析 mount() 系统调用中的参数挂载点路径 pathname ,返回对应的 struct path 结构:

SYSCALL_DEFINE5(mount) → do_mount() → user_path() → user_path_at_empty() → filename_lookup() → path_lookupat() → link_path_walk() → walk_component() → follow_managed()

2、解析 mount() 系统调用中的参数文件系统类型 -t type 和设备路径 devname ,建立起子设备的树形结构(如果之前已经创建过引用即可),建立起新的 struct mount 结构对其引用:

SYSCALL_DEFINE5(mount) → do_mount() → do_new_mount() → vfs_kern_mount() → mount_fs() → type->mount()

3、将新建立的 struct mount 结构挂载到查找到的 struct path 结构上:

SYSCALL_DEFINE5(mount) → do_mount() → do_new_mount() → do_add_mount() → graft_tree() → attach_recursive_mnt() → commit_tree()

2.2 chroot()

更改当前进程的根目录:

SYSCALL_DEFINE1(chroot, const char __user *, filename)
{
struct path path;
int error;
unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
retry:
/* (1) 解析指定的路径,返回对应的`struct path`结构 */
error = user_path_at(AT_FDCWD, filename, lookup_flags, &path);
if (error)
goto out; error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR);
if (error)
goto dput_and_out; error = -EPERM;
if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT))
goto dput_and_out;
error = security_path_chroot(&path);
if (error)
goto dput_and_out; /* (2) 把`struct path`结构设置为当前进程的根目录 */
set_fs_root(current->fs, &path);
error = 0;
dput_and_out:
path_put(&path);
if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL;
goto retry;
}
out:
return error;
} ↓ void set_fs_root(struct fs_struct *fs, const struct path *path)
{
struct path old_root; path_get(path);
spin_lock(&fs->lock);
write_seqcount_begin(&fs->seq);
old_root = fs->root;
/* (2.1) 替换新的根目录 */
fs->root = *path;
write_seqcount_end(&fs->seq);
spin_unlock(&fs->lock);
if (old_root.dentry)
path_put(&old_root);
}

2.3 copy_mnt_ns()

在进程创建或者 unshare()/setns() 系统调用时,如果设置了 CLONE_NEWNS 标志会调用 copy_mnt_ns() 创建一个新的 mnt namespace。其中的核心是创建一颗新的 struct mount 树,首先把旧的 struct mount 树复制过来:

SYSCALL_DEFINE1(unshare) → unshare_nsproxy_namespaces() → create_new_namespaces() → copy_mnt_ns() → copy_tree() → clone_mnt()

随后如果有新的 mount() 动作,两棵树的内容就会不同。

参考文档:

1.Linux Namespace
2.Linux文件系统
3.在CentOS7上使用LXC管理容器
4.docker基础:从chroot理解namespace的隔离
5.Docker基础: Linux内核命名空间之(1)mnt namespace
6.Docker基础:文件系统之AUFS
7.docker image是什么,存储在什么位置
8.Docker镜像存储-overlayfs
9.linux文件系统之mount流程分析
10.dentry和inode的关系

Linux ns 3. Mnt Namespace 详解的更多相关文章

  1. Linux ns 4. UTS Namespace 详解

    目录 1. 使用简介 1.1 hostname 1.2 domainname 1.3 uname 2. 代码分析 2.1 copy_utsname() 2.2 sethostname() 2.3 ge ...

  2. Linux ns 6. Network Namespace 详解

    文章目录 1. 简介 1.1 Docker Network 桥接模式配置 2. 代码解析 2.1 copy_net_ns() 2.2 pernet_list 2.2.1 loopback_net_op ...

  3. Linux ns 5. IPC Namespace 详解

    文章目录 1. 简介 2. 源码分析 2.1 copy_ipcs() 2.2 ipcget() 2.3 ipc_check_perms() 2.4 相关系统调用 参考文档: 1. 简介 进程间通讯的机 ...

  4. Linux下的文件目录结构详解

    Linux下的文件目录结构详解 / Linux文件系统的上层根目录 /bin 存放用户可执行的程序 /boot 操作系统启动时所需要的文件 /dev 接口设备文件目录,例如:had表示硬盘 /etc ...

  5. Linux文件系统的目录结构详解

    Linux文件系统的目录结构详解   一.前 言 文章对Linux下所有目录一一说明,对比较重要的目录加以重点解说,以帮助初学者熟练掌握Linux的目录结构. 二.目 录 1.什么是文件系统 2.文件 ...

  6. Linux文件权限与属性详解 之 ACL

    Linux文件权限与属性详解 之 一般权限 Linux文件权限与属性详解 之 ACL Linux文件权限与属性详解 之 SUID.SGID & SBIT Linux文件权限与属性详解 之 ch ...

  7. Linux下DNS服务器搭建详解

    Linux下DNS服务器搭建详解 DNS  即Domain Name System(域名系统)的缩写,它是一种将ip地址转换成对应的主机名或将主机名转换成与之相对应ip地址的一种机制.其中通过域名解析 ...

  8. Linux自带神器logrotate详解

    Linux自带神器logrotate详解 散尽浮华 运维 3天前   作者:散尽浮华 链接:https://www.cnblogs.com/kevingrace/p/6307298.html 对于 L ...

  9. Linux上的free命令详解、swap机制

    Linux上的free命令详解   解释一下Linux上free命令的输出. 下面是free的运行结果,一共有4行.为了方便说明,我加上了列号.这样可以把free的输出看成一个二维数组FO(Free ...

随机推荐

  1. django 安装与配置-01

    安装命令 sudo pip install django python 可以引入django说明已经安装成功 查看django的版本 查看django管理命令 django-admin django创 ...

  2. django把变量变成字段进行搜索

    from ceshi.models import Student     #引入model中的模型 获取前端请求的参数 searchKey=request.GET.get("key" ...

  3. P5405-[CTS2019]氪金手游【树形dp,容斥,数学期望】

    前言 话说在\(Loj\)下了个数据发现这题的名字叫\(fgo\) 正题 题目链接:https://www.luogu.com.cn/problem/P5405 题目大意 \(n\)张卡的权值为\(1 ...

  4. 密码学系列之:1Password的加密基础PBKDF2

    目录 简介 PBKDF2和PBKDF1 PBKDF2的工作流程 详解PBKDF2的key生成流程 HMAC密码碰撞 PBKDF2的缺点 总结 简介 1password是一个非常优秀的密码管理软件,有了 ...

  5. 《手把手教你》系列技巧篇(二十八)-java+ selenium自动化测试-处理模态对话框弹窗(详解教程)

    1.简介 在前边的文章中窗口句柄切换宏哥介绍了switchTo方法,这篇继续介绍switchTo中关于处理alert弹窗的问题.很多时候,我们进入一个网站,就会弹窗一个alert框,有些我们直接关闭, ...

  6. SphereEx 创始人张亮云咖访谈回顾:构建数据服务的新思路

    2021 年 7 月 21 日,2021 亚马逊云科技中国峰会在上海盛大开幕.本次大会以"构建新格局,共赢云时代"为主题,邀请到来自技术社区.开源软件基金会.开源创业代表.女性开发 ...

  7. SpringBoot整个Druid

      Druid简介 Java程序很大一部分要操作数据库,为了提高性能操作数据库的时候,又不得不使用数据库连接池. Druid 是阿里巴巴开源平台上一个数据库连接池实现,结合了 C3P0.DBCP 等 ...

  8. 用C++实现的数独解题程序 SudokuSolver 2.2 及实例分析

    SudokuSolver 2.2 程序实现 根据 用C++实现的数独解题程序 SudokuSolver 2.1 及实例分析 里分析,对 2.1 版做了一些改进和尝试. CQuizDealer 类声明部 ...

  9. 手把手教你写hexo博客

    市面上现在有各种博客框架,本博客教大家的是Hexo博客框架,目前比较火.搭建博客中遇到各种各样问题,网上方案也比较成熟. 一.搭建环境 安装 git 安装 node.js 安装 Hexo npm in ...

  10. 对epoll机制的学习理解v1

    epoll机制 wrk用非阻塞多路复用IO技术创造出大量的连接,从而达到很好的压力测试效果.epoll就是实现IO多路复用的关键. 本节是对epoll的本质的学习总结,进一步的参考资料为: <深 ...