Linux系统——EXT2 文件系统

发布于:2025-08-30 ⋅ 阅读:(21) ⋅ 点赞:(0)

磁盘

文件 = 文件属性 + 文件内容

文件内容 —— 数据块,文件属性 —— inode

Linux 文件在磁盘中的存储,是将 属性 与 内容 分开存储的

内存:掉电易失,磁盘:永久性存储介质

图片来自百度

磁盘访问的基本单元:扇区 —— 每个扇区一般存储 512 byte / 4 kb 数据

磁盘由无数个扇区构成,定位扇区的步骤(CHS Cylinder-Head-Sector 寻址方式):6个磁头(Head)定位,哪一个磁道 / 柱面(Cylinder),定位扇区(Sector)

但现代大容量硬盘已改用 LBA(Logical Block Addressing)线性寻址方式:将磁盘在逻辑上 抽象成 线性结构 —— 无数个扇区组成,每个扇区都有自己的下标

LBA <=> CHS

这两种地址可以相互转换,但使用者直接使用 LBA 地址,磁盘内部自己转换为 CHS地址寻找。

逻辑上,磁盘就是一个 元素为 扇区 的一维数组,数组的下标 就是每一个 扇区的 LBA地址,操作系统就可以用 一个数字(下标) 来访问磁盘的扇区。

文件系统

块 概念

硬盘是典型的 块 设备,操作系统读取硬盘数据时,不会一个个扇区进行读取;为提高效率,一次性读取一个 块,一个 块 包含连续的多个扇区。

硬盘的每个分区 是被划分为多个 块;

一个 块 包含几个连续扇区,是由格式化的时候确定的,不可更改,常见一个块大小 4 KB,即 8个连续扇区(512 b)组成一个 块。

块 是文件读取的最小单位。

分区 概念

一个磁盘被分为多个分区(C、D、E),分区实质上说,是对硬盘的一种格式化

如何分区?

柱面 是分区的最小单位(我觉得 磁道 更形象),本质就是设置 每个分区的 起始柱面和结束柱面 号码。

每个分区的 boot sector 启动块 大小确定,1 KB,用以存储 磁盘分区信息和启动信息。

inode 概念

每个文件的 文件数据 都储存在 块 中,对应的 属性信息 储存在 inode 中(不包括文件名),一个 inode 有一个唯一标识符 —— inode 号(用以标识文件),一般 128 B 大小。

block group 块组

ext2 文件系统根据 分区的大小划分为多个 block group。

super block 超级块

存放文件系统本身的结构信息,描述整个分区的文件系统信息:

block 和 inode 的总量、未使用的数量、一个 block 和 inode 的大小,最近一次的挂载时间,最近一次写入数据的时间,最近一次检验磁盘的时间 等其他文件系统的相关信息。

所以 super block 的数据如果被破坏,整个文件系统结构就被破坏了;

但是,为了保证文件系统在磁盘的 部分扇区 出现物理问题时还能工作,一个 文件系统 的 super block 在 每个或者多个 block group 中进行备份,且数据保持一致。

GDT 块组描述符表

Group Descriptor Table 块组描述符表,表中包含多个 块组描述符,每个 描述 一个 块组 的属性信息:

这个块组从哪里开始是 inode table,从哪里开始是 data blocks,还有多少空闲的 inode、data blocks。

注意:整个文件系统只有一份“块组描述符表”(Group Descriptor Table)。
这张表通常放在 块组 0(有时是块组 0 和 1 的备份) 里,其余所有块组不再存放完整的描述符表副本。(ext4 为了冗余会把描述符表做备份,但也只是少数几个块组有,并非“每个块组都相同”。)

data blocks 数据块

存放文件内容的区域,以 块 的形式呈现,常见 一个 块(4 KB) 大小为 8 个 扇区(512 B);

文件系统的 块 必须 ≥ 扇区大小,起到 预加载 的作用,提高读取效率。

对普通文件:数据存储在 数据块 中;

对目录:该目录下的所有文件名和目录名 存储在 所在目录的 数据块 中,除了文件名外,ls -l 命令看到的其他文件信息,保存在 该文件的 inode 中。

数据块中的 block 块 按照分区整体划分,不可跨分区。

inode table 节点表

当前分区 所有 inode 的集合,inode table 中的 inode 编号以分区为单位,整体划分,不可 跨分区。

每个 inode 的结构中,会包含 12 个直接数据块指针,和几个 一级、二级、三级间接块指针:这样保证了 大文件的存储

block bitmap 块位图

位图中的 bit 位 的位置 与 块号 的形成映射,bit 位 的内容,表示 该位置 对应的 块 是否被使用;

所以,删除一个文件时,无须清空其 data blocks 的内容,只需找到这个文件的 inode,inode 中有指向该文件的 块指针、大小 等信息,将其 数据块 对应的 块位图 中的位置 的 bit位 置空,就算是在 逻辑上 删掉了。

inode bitmap inode位图

和 块位图 类似,表示 inode table中 对应位置的 inode 是否被使用;

所以,删除一个文件时,对其 block bitmap 块位图 和 inode bitmap inode位图 做处理即可,提升效率。

小结

每个分区相互 独立、包括 inode 编号、数据块编号 等、

一个分区中可用的 inode 总数,是确定的,用完了就没办法了(有可能 inode 用完了,data block 块还有,因为 inode 是确定的,预分配好的;也有可能 data block 用完了,但是 inode 还有)

每个 块组 中能用多少个 inode ,块组最开始的 super block、GDT 中有相应信息。

单个文件 = 一个 inode + 若干data blocks

(先描述,再组织)

格式化 : 每个分区在被使用前,都必须提前将部分文件系统的属性信息,提前设置进对应的分区中,以便后续使用

inode 表征了文件的所有属性,但是,文件名并不属于文件属性!

文件的增删查改

新建文件

通过新建文件的路径:确定在哪一个分区里 —— 然后查 GDT,inode 使用情况 —— 查 inode bitmap,分配空闲 inode

—— 填入文件属性 —— 准备写入 —— 确认写入的数据大小(例如 write 中有参数问你 写入数据大小)

—— 查block bitmap ,分配数据块 —— 并把数据块的编号,填入 inode 的属性之一:与数据块的 映射数组

—— 然后通过映射数组,把数据写入到对应的数据块中

删除文件

数据内容删除:inode 的 与 该文件所需的数据块 的 映射数组中 找到该文件的 所有数据块,将这些数据块编号 对应的 block bitmap 中 置零

inode文件属性删除 : 然后 inode bitmap 也置零

删除 = 允许被覆盖

为什么不用清空data block 和 inode 内容的原因:过多的 IO 会大幅降低系统效率

数据恢复:数据块被覆盖之前,都可以想办法恢复

查找文件

根据文件路径确定 分区 —— 根据文件名 确定 inode编号,根据 inode编号 确定是哪个块组

—— 根据 inode 编号,查 inode bitmap 确定是否有效文件

—— 从 inode bitmap 索引到 在 inode table 中的该 inode

—— 在 该 inode 中,找到 与数据块的 映射数组(也就是块指针,直接的或者间接的),

—— 将 映射数组中的 数据块 的内容,载入内存(注意是整个数据块,数据不一定占满了整个数据块)(当然如果文件过大,应该遵循 惰性加载)

—— 因为文件有大小,inode中存储了占用的字节数,所以从该数据块开始,读取 文件大小的 字节

修改文件

先查找文件,找到后加载进内存,然后 IO

目录文件

linux 下,一切皆文件!

文件 = 内容 + 属性

目录也是文件,有自己独立的 inode,有自己的属性(上一个图中 蓝色就是目录文件)

那么

目录的数据块存什么?

存的是 该目录下,文件的 文件名和 对应文件 inode 编号的 映射关系

1、所以 同一个目录下,不能有同名文件,否则会冲突

2、目录中 如果没有 w 权限,无法创建文件:因为 这个文件的 inode编号 与 文件名的 映射关系,就写不进 目录文件中

3、目录下没有 r 权限,无法查看文件

4、目录下,没有 x 可执行权限,无法进入这个目录

可是 —— 目录文件的 inode 怎么获取?

得从上一级 已经打开的目录文件中 找, 一路找到 根目录,然后从目录一路返回

类似 递归, 需要把路径中所有的目录全部解析,出口是 / 根目录

Linux 路径解析 : 实际上,任何文件的打开,都从 该文件路径的 根目录开始,依次打开每一个目录,依次访问

系统会对 常见的常打开的目录文件的 inode 进行 dentry缓存,这样提高效率

所以在,访问任何文件时,都需要有路径

软硬链接

软链接

首先,软链接是 一个独立的文件,具有独立的 inode

ln 命令:建立链接,从 后者,指向 前者

-s : 建立 软链接

软链接 的文件的 内容:所指向文件的 路径

硬链接

硬链接 不是一个独立的文件,因为 没有独立的 inode

ln 命令:建立硬链接,从 后者,指向 前者

建立硬链接之后,该文件 inode 属性中的 引用计数,变为 2,表示有多少个文件名指向本文件

并且,硬链接的inode 与 源文件一样,这表示,就是同一个文件

硬链接干了什么? :本质就是 在特定目录的数据块中,新增 文件名和指向文件的 inode编号的 映射关系

这也表明 多个文件名可以映射 同一个 inode编号

对比分析

每个 inode 内部都有一个 引用计数的 计数器 ( 有多少个 文件名 指向本 inode)

引用计数-- 直到 0 ,文件才会被删掉

硬链接文件,会增加该文件的引用计数。

软连接不影响 该文件的 引用计数,软链接像是 windows 中的 ‘快捷方式',数据块里保存了指向文件的 路径。

应用

软链接

可以让文件更简单的访问、可以链接一个 目录很深的可执行程序、

硬链接

以上图为例,linux 目录中,  . 当前目录 是 /home/suo/test_git/linux/0825  的硬链接、、 .. 上级目录 是  /home/suo/test_git/linux   的硬链接、

为什么 . 当前目录 的引用计数是 5 ?

因为 当前目录中,我有 5 个文件,每个文件中都有 2 个隐藏的硬链接 .  和 .. ,这个 .. 就是我当前目录 /home/suo/test_git/linux/0825  的硬链接。

为什么 .. 上级目录的引用计数是 23 ?

因为 在 上级目录  /home/suo/test_git/linux  中,有 23个文件,每个文件里都有个 .. 上级目录  的硬链接,包括这个目录  /home/suo/test_git/linux/0825  

所以 cd .. 的细节实现 :

.. 是上级目录 /home/suo/test_git/linux 的硬链接 —— 所以从根目录一路解析,获取到了 目录文件 linux/  的 inode —— 所以 这个 inode 的路径也就获得了,切换到 上级目录的路径即可

所以硬链接 —— 可以维持 linux 下的 目录结构

但是 :linux 中 不允许对目录建立硬链接,容易出现 环 的问题

但 . 和 .. 本身就是目录的硬链接,所以 这两个硬链接是隐藏文件,而且,系统在搜索文件时,默认不会搜索这两个 硬链接 的 目录文件。避免了环的问题


网站公告

今日签到

点亮在社区的每一天
去签到