Ext文件系统(2)软件系统 一、宏观图景分区 → 块组 → 六件套一块磁盘可以划分多个分区每个分区可以有自己独立的一套文件系统ext2/3/4、NTFS……即文件系统以分区为单位。分区内部再按Block Group块组切分每个块组包含六件套组件作用Super Block文件系统全局信息分区有多大、块组怎么分GDTGroup Descriptor Table块组描述符表描述每个块组Block Bitmap块位图哪个数据块被占用inode Bitmapinode 位图哪个 inode 被占用inode Tableinode 表存所有 inode属性Data Blocks数据块存文件内容二、块Block为什么是 4KB磁盘物理扇区是512B但操作系统读写的基本单位是4KB 的块Block为什么不用 512B软硬件解耦磁盘技术更新512B → 4K 扇区OS 不必跟着改偶合度低效率考量小粒度读写导致 IO 次数暴增 内存管理的页也是 4KB ——磁盘块天然就是为载入内存准备的三、inode文件的属性卡片inode struct inode 结构体对象大小固定128 字节直接以二进制写盘字段类型固定类型 int、权限 char/short、大小 long long……为什么文件名不存 inode✖ 文件名是变长字符串会破坏 128B 固定大小的设计✔ 文件名保存在所属目录的数据块中见第五节inode Table本身也是数据块一块 4KB 4096 / 128 32 个 inode访问一个文件属性时文件系统把整个 4KB 块搬进内存——一次读 32 个 inode 局部性原理跟 vector 扩容一个思路多拿一点备着inode 编号每个文件有唯一编号ls -li查看根目录 inode 编号 2编号 0、1 保留四、位图与超级块管理数据的管理数据文件系统不仅要存文件还要管理哪些块/哪些 inode 被用了——于是有了管理数据的管理数据。4.1 Block Bitmap块位图1 个比特对应 1 个数据块比特为 1 → 占用为 0 → 空闲10 万个 DataBlock → 位图 10 万比特 ≈3~4 个数据块申请数据块 对应比特置 1释放 清零4.2 inode Bitmapinode 位图同理1 个比特对应 1 个 inode删除/恢复文件的本质删除 两个位图中对应比特清零属性和内容根本没动恢复 把比特重新置 1只要数据块没被别人覆盖所以删除是 O(1) 的逻辑删除这也是误删文件有可能被救回来的原理。4.3 GDT块组描述符描述每一个块组的信息该组的位图在哪、inode 表在哪、空闲块多少……4.4 Super Block超级块描述整个分区/文件系统的全局信息块大小、块组数量、inode 总数……✖ 它是全局唯一的一旦损坏整个分区报废——所以多个块组里都有它的备份4.5 格式化的本质✔ 格式化 ≠ 清空内容而是向分区写入文件系统的管理信息SuperBlock / GDT / 位图 / 分组结构4.6 inode 定位公式已知 inode 编号 N块组号 N / 每组inode数组内偏移 N % 每组inode数开机时 OS 把管理信息SuperBlock、位图等加载进内存——先描述、再组织五、目录的本质也是文件震惊点目录和普通文件的存储方式完全一样inode 数据块只是 inode 的 type 字段不同。目录的数据块里存的是一张映射表该目录下所有「文件名 ↔ inode 编号」磁盘上没有目录这个概念只有 inode block靠 type 区分同目录下文件名唯一文件名唯一 inode 唯一互为键值所以磁盘层面无重复六、路径解析进程文件的产生和缓存的底层全流程大体思路进程task_struct)中有个存了指向file_struct指针的结构体结构体里面的指针都是指向跟当前进程相关的文件通过表找到对应的file_structfile_struct中还存在了dentry结构体通过dentry结构体可以找到对应的其他dentry结构体最后组成缓存多叉树6.1 谁提供路径文件名用户提供的路径进程提供的——进程 PCB 里有 cwd当前工作目录由 bash 继承而来最终源自系统环境 PWD用户文件名 cwd → 拼出绝对路径6.2 解析流程打开当前目录 → 读它的数据块映射表找到文件名对应的 inode 编号编号 → 除模定位块组 → 查 inode Bitmap → 找到 inode Table 中的位置把 inode 属性加载进内存 → 打印路径解析必须从根目录开始递归访问文件要先打开所在目录找目录又要找上级目录……一路递归到根根目录在开机时就被 OS 打开了这是一切的起点6.3 效率问题 → dentry 缓存多叉树每次都从根开始解析 大量磁盘 IO太慢✔ Linux 在内存中动态构建目录树缓存每一个节点都是一个dentry结构体历史访问过的路径形成一棵多叉树他们通过父子指针连在一起。struct dentry关键字段引用计数、flags、inode 指针、d_parent、subdirs、LRU 链表、lookup 哈希链一个节点多重挂靠既在多叉树上路径查找、又在 LRU 链表上淘汰、又在哈希表上快速查找实测find /第一次慢树不完善要磁盘 IO第二次快内存命中七、挂载mount分区与目录的绑定7.1 为什么要挂载inode 编号不能跨分区不同分区各有一套编号那我怎么知道文件在哪个分区——分区必须与一个目录关联起来才能使用这就是挂载cd 进入挂载点目录 进入那个分区7.2 实操dd 制作伪磁盘 挂载知道原理即可# 1. 造一个 5MB 的大文件当磁盘ddif/dev/zeroofdisk.imgbs1MBcount5# 2. 格式化成 ext4写入管理信息mkfs.ext4 disk.img# 输出关键信息block size 10241280 inodes5120 blocks# 3. 挂载到一个空目录mkdirdirmount-text4 disk.imgdir# 4. 查看挂载情况df-h# 5. 卸载umountdir7.3 路径前缀最近匹配原则文件路径前缀含哪个挂载点文件就在哪个分区嵌套挂载时用最近最深前缀匹配八、fopen 时 OS 做了什么全流程串联路径解析查 dentry 树 → 找到 inode → 内核创建 struct file含缓冲区、操作表填充 → 创建 struct inodeinode 属性加载进内存 → fd 表分配 → 返回 fd用户拿到 fd后续 read/write 全靠它 这就是一切文件操作先打开、后使用的底层含义九、软链接与硬链接9.1 硬链接hard linkln 目标 新名字同一个 inode多个文件名——目录映射表里多条记录指向同一 inode 编号inode 里的链接计数记录有几个名字指向它删除一个名字 计数减 1计数归 0 才真正释放位图清零✖ 不能跨分区inode 编号不跨分区、不能对目录建硬链接9.2 软链接soft link / 符号链接ln -s 目标 新名字独立的 inode独立文件数据块里存的是目标的路径字符串访问软链接 读出路径 → 重新做路径解析软链接的用处为什么要软链接可以跨分区、可以指向目录可以把一个可执行程序快捷方式到当前目录下方便我们执行。软链接目标被删后成为悬空链接9.3 对比硬链接软链接inode与目标同一个独立inode本质目录项中的映射记录独立文件存目标路径跨分区✖✔链接目录✖✔目标删除后依然可用计数未归 0悬空失效十、知识点小总结问题答案文件系统以什么为单位分区分区内再切块组块多大4KB内存页同款软硬件解耦inode 多大存什么128B存属性不存文件名一块 inode Table 多少 inode4096/128 32 个文件名存哪所属目录的数据块文件名↔inode 映射表删除文件的本质位图清零数据不动格式化的本质写入管理信息SB/GDT/位图/分组inode 怎么定位编号除模组号 N/每组数偏移 N%每组数路径解析从哪开始根目录开机已被 OS 打开dentry 缓存加速分区怎么用挂载到目录前缀最近匹配定分区硬链接/软链接硬同 inode 计数软独立 inode 存路径