百篇博客系列篇.本篇为:
v65.xx 鸿蒙内核源码分析(挂载目录篇) | 为何文件系统需要挂载
文件系统相关篇为:
- v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么说一切皆是文件
- v63.xx 鸿蒙内核源码分析(文件系统篇) | 用图书管理说文件系统
- v64.xx 鸿蒙内核源码分析(索引节点篇) | 谁是文件系统最重要的概念
- v65.xx 鸿蒙内核源码分析(挂载目录篇) | 为何文件系统需要挂载
- v66.xx 鸿蒙内核源码分析(根文件系统) | 先挂到
上的文件系统/
- v67.xx 鸿蒙内核源码分析(字符设备篇) | 字节为单位读写的设备
- v68.xx 鸿蒙内核源码分析(VFS篇) | 文件系统和谐共处的基础
- v69.xx 鸿蒙内核源码分析(文件句柄篇) | 你为什么叫句柄 ?
- v70.xx 鸿蒙内核源码分析(管道文件篇) | 如何降低数据流动成本
关于文件系统的介绍已经写了三篇,但才刚刚开始,其中的 [文件系统篇] 一定要阅读,用生活中的场景去解释计算机各模块设计的原理和运行机制是整个系列篇最大的特点,计算机文件系统相关概念是非常的多的,若不还原其本质,不跳出这些概念去看问题是很难理解它为什么要弄这么些东东出来让你头大. 反之,如果搞明白了这些概念背后的真相你想忘记它们都很难,问题是经不起追问的, 多追问几个为什么就会离本源越来越近.
前几篇中追问了以下几个问题:
- 对内核来说
真的是唯一的吗? 答案是否定的,使用电脑的经验告诉我们,当把电脑硬盘拆下来挂到其他电脑上时,里面的数据一样能访问,并没有让你一切重来,而inode
是存放硬盘上的,你没有办法让已编好序号的inode
按你的逻辑重排,这不合理更不科学. 所以结论是inode
的全局唯一性不是不想做,而是压根臣妾做不到啊.inode
唯一性仅限于某个文件系统的内部.inode
- 经验还告诉我们硬盘可以有多个分区,每个分区可以被格式化成不同的文件系统.(例如:C盘:
,D盘NTF
,E盘FAT32
),数据可以相互拷贝,毫无障碍.不同的文件系统是如何实现文件迁移到呢 ? 具体实现细节是怎样的 ?ext
如果想明白了这些问题, 就能逆向倒推为什么要有目录,为什么需要挂载使用, 为什么需要根文件系统.一切将是水到渠成 .
先说目录,从内核视角看目录可不能像普通老百姓从用户视角去看,目录是为了屏蔽文件系统之间的差异而设计出来的概念,也就说必须在
inode
的局部唯一性之上存在一个全局唯一性才能解决统一性问题.目录从更大尺度上去兼容并蓄各文件系统.
那它是如何解决的呢?
-
首先各个文件系统记录了自己内部目录层级关系的,这个在 文件系统篇 | 目录项 中已经说过了. 这种关系是绝对的但也是相对的,绝对是对内,相对是对外. 例如:
A文件系统内部如下:
B文件系统内部如下:├─古龙系列 inode id : 789 │ ├─小李飞刀 inode id : 56 │ ├─楚留香 inode id : 342 │ └─陆小凤 inode id : 432 └─金庸系列 inode id : 5567 ├─倚天屠龙记 inode id : 89 ├─射雕英雄传 inode id : 1212 └─笑傲江湖 inode id : 567843
其中├─席绢系列 : inode id : 87 │ ├─上错花轿嫁对郎 : inode id : 89 │ ├─吻上你的心 : inode id : 789 │ └─红袖招 : inode id : 56 └─琼瑶系列 : inode id : 321 ├─在水一方 : inode id : 234 ├─梅花三弄 : inode id : 5678 ├─烟雨濛濛 : inode id : 987 └─还珠格格 : inode id : 23
,789
两个文件系统中都用到了,但它们在内部是唯一的.在A文件系统中通过89
就能找到789
,56
,342
,并且能得到相对路径: 古龙系列/小李飞刀,古龙系列/楚留香 .也就是说拿着432
只要进入了本文件系统地盘,那都不叫事,事都能给你办的妥妥的. 那如何才能进入而且不会搞错呢?inode
挂载目录
答案就是: 挂载目录,也叫挂载点,集体统一指挥的前提是需要先回归集体.如果已经有一颗目录树,将你们的目录树挂上来形成一颗更大的树不就统一了吗? 例如已有:
├─小说系列 inode id : 2
│ ├─武侠小说 inode id : 13
│ ├─言情小说 inode id : 14
其实它也是个文件系统,叫根文件系统, 它的
inode
也是独立的, 并且能得到相对路径
小说系列/武侠小说
,
小说系列/武侠小说
通过两个
mount
动作, 将它变成如下所示
├─小说系列 (根文件系统)
├─武侠小说 (根文件系统)
│ ├─古龙系列 (A文件系统)
│ │ ├─小李飞刀
│ │ ├─楚留香
│ │ └─陆小凤
│ └─金庸系列 (A文件系统)
│ ├─倚天屠龙记
│ ├─射雕英雄传
│ └─笑傲江湖
└─言情小说 (根文件系统)
├─席绢系列 (B文件系统)
│ ├─上错花轿嫁对郎
│ ├─吻上你的心
│ └─红袖招
└─琼瑶系列 (B文件系统)
├─在水一方
├─梅花三弄
├─烟雨濛濛
└─还珠格格
哦,原来整颗目录树是由这三个文件系统像搭积木一样拼接起来.而两个文件系统的衔接点,必然会产生一个新的概念出来, 这个概念就是 挂载点,也叫 挂载目录
Mount
可以猜测到的是挂载点的描述结构体中必有两个文件系统接驳点
inode
的信息,挂钩和脱钩的操作也只属于它专有.具体如下:
//挂载操作
struct MountOps {
int (*Mount)(struct Mount *mount, struct Vnode *vnode, const void *data);//挂载
int (*Unmount)(struct Mount *mount, struct Vnode **blkdriver);//卸载
int (*Statfs)(struct Mount *mount, struct statfs *sbp);//统计文件系统的信息,如该文件系统类型、总大小、可用大小等信息
};
struct Mount {
LIST_ENTRY mountList; /* mount list */ //通过本节点将Mount挂到全局Mount链表上
const struct MountOps *ops; /* operations of mount */ //挂载操作函数
struct Vnode *vnodeBeCovered; /* vnode we mounted on */ //要被挂载的节点 即 /bin1/vs/sd 对应的 vnode节点
struct Vnode *vnodeCovered; /* syncer vnode */ //要挂载的节点 即/dev/mmcblk0p0 对应的 vnode节点
LIST_HEAD vnodeList; /* list of vnodes */ //链表表头
int vnodeSize; /* size of vnode list */ //节点数量
LIST_HEAD activeVnodeList; /* list of active vnodes */ //激活的节点链表
int activeVnodeSize; /* szie of active vnodes list *///激活的节点数量
void *data; /* private data */ //私有数据,可使用这个成员作为一个指向它们自己内部数据的指针
uint32_t hashseed; /* Random seed for vfs hash */ //vfs 哈希随机种子
unsigned long mountFlags; /* Flags for mount */ //挂载标签
char pathName[PATH_MAX]; /* path name of mount point */ //挂载点路径名称 /bin1/vs/sd
char devName[PATH_MAX]; /* path name of dev point */ //设备名称 /dev/mmcblk0p0
};
解读
-
: 挂载点由双向链表全局统一管理mountList
-
:,记录挂到根文件系统的哪个节点上.vnodeBeCovered
-
: 设备也是一种文件,也被统一管理,统一在vnodeCovered
目录下,内核会给设备的每个分区分配一个/dev
节点,一个分区对应一个文件系统,设备文件后续有专门的介绍,此处不展开.vnode
-
: 指的是A/B文件系统的节点链表,由挂载点结构体记录.vnodeList
-
:activeVnodeList
文件系统节点的使用情况,统一由双向链表管理.A
-
:activeVnodeSize
文件系统已被使用的节点数A
-
这是文件系统的私有数据,跟 索引节点篇 | Vnode -> data 一样理解.data
-
:这个很重要,记录了pathName
,因为文件的绝对路径是拼接起来的,以小说系列/武侠小说
这个完整的路径来说,它是由小说系列/武侠小说/古龙系列/小李飞刀
(根文件系统提供) +小说系列/武侠小说
(A文件系统提供) 这两部分拼成的.古龙系列/小李飞刀
-
:一般名称类似于devName
=mmcblk0p0
+mmc
+block0
Partition0
-
:mmc
可理解为硬盘MultiMediaCard
-
: 0号块设备block0
-
:0号分区,一个分区上安装一个文件系统.Partition
-
-
: 每个文件系统挂载方式是不用的,都需要实现这几个接口(挂载,卸载,统计).MountOps ops
// 文件系统 proc 对 MountOps 接口实现 const struct MountOps procfs_operations = { .Mount = VfsProcfsMount,//装载 .Unmount = NULL, .Statfs = VfsProcfsStatfs,//统计信息 }; //文件系统 fat 对MountOps 接口实现 struct MountOps fatfs_mops = { .Mount = fatfs_mount, .Unmount = fatfs_umount, .Statfs = fatfs_statfs, }; //文件系统 jffs 对MountOps 接口实现 const struct MountOps jffs_operations = { .Mount = VfsJffs2Bind, .Unmount = VfsJffs2Unbind, .Statfs = VfsJffs2Statfs, };
问题
上面提到 挂载就需要一个已经存在的文件系统提供目录,也就是根文件系统,但根文件系统又是怎么来的呢?
百篇博客分析.深挖内核地基
给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,
.xx
代表修改的次数,精雕细琢,言简意赅,力求打造精品内容。
编译构建 | 基础工具 | 加载运行 | 进程管理 |
---|---|---|---|
编译环境篇 编译过程篇 环境脚本篇 构建工具篇 gn应用篇 忍者ninja篇 | 双向链表篇 位图管理篇 用栈方式篇 定时器篇 原子操作篇 时间管理篇 | ELF格式篇 ELF解析篇 静态链接篇 重定位篇 进程映像篇 | 进程管理篇 进程概念篇 Fork篇 特殊进程篇 进程回收篇 信号生产篇 信号消费篇 Shell编辑篇 Shell解析篇 |
进程通讯 | 内存管理 | 前因后果 | 任务管理 |
自旋锁篇 互斥锁篇 进程通讯篇 信号量篇 事件控制篇 消息队列篇 | 内存分配篇 内存管理篇 内存汇编篇 内存映射篇 内存规则篇 物理内存篇 | 总目录 调度故事篇 内存主奴篇 源码注释篇 源码结构篇 静态站点篇 | 时钟任务篇 任务调度篇 任务管理篇 调度队列篇 调度机制篇 线程概念篇 并发并行篇 系统调用篇 任务切换篇 |
文件系统 | 硬件架构 | ||
文件概念篇 文件系统篇 索引节点篇 挂载目录篇 根文件系统 字符设备篇 VFS篇 文件句柄篇 管道文件篇 | 汇编基础篇 汇编传参篇 工作模式篇 寄存器篇 异常接管篇 汇编汇总篇 中断切换篇 中断概念篇 中断管理篇 |