【Linux】文件系统(一)

目录

磁盘的理解

存储结构

相关概念

CHS和LAB

CHS

LAB

公式转换

文件系统

初步

"块"的概念

"分区"

iNode

块组内部

[Super Block(超级块)](#Super Block(超级块))

[GDT(Group Descriptor Table)](#GDT(Group Descriptor Table))

[Block Bitmap(块位图)](#Block Bitmap(块位图))

[Inode Bitmap(inode 位图)](#Inode Bitmap(inode 位图))

[Inode Table(索引节点表)](#Inode Table(索引节点表))

[Data Blocks(数据块)](#Data Blocks(数据块))

疑问


磁盘的理解

存储结构

相关概念

拆开一个机械硬盘,你会看到里面有几个关键部件:

  1. 盘片(Platter):像光盘一样闪闪发光的圆盘。数据就刻在上面。

  2. 磁头(Head) :悬在盘片上方极近的地方,负责读取和写入数据。重点:每个盘面都有一个磁头,但所有的磁头都连在同一个机械臂上,同进同退!

  3. 主轴(Spindle):带着盘片飞速旋转的马达。

为了在这么大的盘片上定位数据,工程师发明了三个概念:

  • 磁道(Track):把盘片想象成操场跑道,一圈一圈的同心圆就是磁道。

  • 扇区(Sector) :把跑道切成一段一段的,每一小段就是一个扇区。这是磁盘存储的最小物理单位,通常是 512 字节。

  • 柱面(Cylinder) :因为所有盘片的磁头是同进同退的,所以当你把磁头停在某个位置时,所有盘面上对应位置的磁道在空间上叠在一起,形成了一个圆柱体,这就是柱面。

CHS和LAB

CHS

如果让你去拿数据,你需要告诉机械臂:

  1. 去第几个柱面(C)?

  2. 用第几个磁头(H)?

  3. 读第几个扇区 (S)?

    这就叫 CHS 寻址。但这种方法很反人类,因为你要同时管理三个数字,而且外圈磁道和内圈磁道能切的扇区数量还不一样(早期)。

⼀个细节:传动臂上的磁头是共进退的

LAB

操作系统是个"大忙人",它不想管你里面有几个盘片、磁头怎么动。它希望用最简单的方式管理存储。

所以,操作系统把整个磁盘**"拍扁"** ,看成一根长长的**"录音磁带"**。

  • 磁带上密密麻麻排满了等大的格子。

  • 每个格子就是一个扇区。

  • 每个格子都有一个唯一的编号(从 0 开始),这个编号就叫 LBA(Logical Block Address,逻辑块地址)。

操作系统只需要说:"我要读第 1000 号扇区(LBA=1000)",然后把它交给磁盘控制器。
磁盘内部的固件(伺服系统)会自己用数学公式,瞬间把 LBA 1000 转换成对应的 CHS(柱面、磁头、扇区),然后机械臂精准地去拿数据。

公式转换

假设磁盘有 100 个柱面,4 个磁头,每磁道 8 个扇区。

想象你在操场上找自己的位置(LBA),你要先算前面有几个完整的小队。

  1. C 算前面有几排 :每个柱面(排)有 4 × 8 = 32 个扇区。

    LBA = C × 32 + H × 8 + (S - 1)

    (注:S 减 1 是因为物理扇区从 1 开始数,而 LBA 从 0 开始数)

  2. 反推 :

    C = LBA ÷ 32 (算排在前面有几排)

    H = (LBA 剩下的余数) ÷ 8 (算这一排里,前面有几层)

    S = (LBA 剩下的余数) % 8 + 1 (算这一层里,第几个座)

所以:OS 只认 LBA,磁盘硬件自己转换成 CHS。

文件系统

初步

"块"的概念

物理磁盘存取的最小单位是扇区(Sector),通常只有 512 字节。如果你有一本 10MB 文件,需要读 2 万次扇区。磁盘机械臂疯狂寻道,效率低。

  • 块是操作系统读写磁盘的最小单位。

  • 就像图书馆的"书箱",借书还书都以"箱"为单位。

  • 这样一来,读取一个 10MB 的文件,只需搬运 2500 次(10MB / 4KB),效率直接提升 8 倍!

"分区"

假设你有一块 1TB 的硬盘,如果把它当成一个大整体来管理,所有的文件、系统备份、用户数据全混在一起。一旦系统崩溃,重装系统可能会把数据全毁掉;而且寻找文件时,面对 1TB 的空间,头都要炸了。

【引入分区】

就像一个大图书馆,如果只有一个大厅,找书太累了。

管理员决定用承重墙把大厅隔开,分成:科技区(C盘) 、历史区(D盘)。

  • 分区,就是把一个大硬盘切成几个逻辑上独立的区域。

  • 每个分区可以单独格式化、单独装系统、单独管理。

  • 操作系统只需要分别管理这几个独立的小区域,复杂度大幅降低。

iNode

在分区里,数据已经被打散成一个个"块"了。假如你写了一个 100KB 的 C 语言代码文件 main.c,它会被切成 25 个 4KB 的块,散落在硬盘的各个角落。

操作系统怎么知道这 25 个块属于 main.c 呢?

引入 inode】

在图书馆里,为了找到一本书,你必须查**"图书检索卡"**。

  • 这张卡片上写着:书名、作者、出版时间(文件的属性)。

  • 最重要的是,卡片上写着:这本书分别放在了第几排、第几个书架的哪几个箱子里 (指向文件数据块的指针)。

这张"图书检索卡",在 Linux 中就叫 inode(索引节点)。

  • 一个文件对应唯一的一个 inode。

  • inode 存储了文件的所有属性(大小、权限、创建时间等),但唯独没有文件名。

  • inode 内部有一个指针数组,指向了存储该文件内容的那些 数据块(Block)。

块组内部

Super Block(超级块)

cpp 复制代码
struct ext2_super_block {
    __le32 s_inodes_count;       // 整个分区有多少个 inode
    __le32 s_blocks_count;       // 整个分区有多少个 4KB 块
    __le32 s_free_inodes_count;  // 还剩多少空闲 inode
    __le32 s_free_blocks_count;  // 还剩多少空闲数据块
    __le32 s_log_block_size;     // 块大小(比如 4KB)
    // ...
};

详细解释:

如果你格式化了一个 300GB 的分区,Super Block 里就记着:"本分区总共有 7864 万个数据块,总共有 196 万个 inode,目前全都是空的。"

协作情况 :它只管全局宏观数据。如果有进程想创建一个文件,内核会先看一眼 Super Block,确认"嗯,还有空闲的 inode 和块",才允许创建。

格式化是在磁盘上写入管理信息。

格式化步骤

bash 复制代码
1. 划分 Block Group
把 300GB 切成一块块的区域(比如每个区 128MB),划分为 Group 0, Group 1, Group 2...

2. 建立"总账本"(写入 Super Block)
在 Group 0 的开头,写入 Super Block。记录:"本分区总共有 7864 万个 4KB 块,有 196 万个 inode。块大小是 4KB。"等信息。

3. 建立"索引目录"(写入 GDT)
写入 Group Descriptor Table,告诉系统:Group 0 的位图在哪个块,Group 1 的位图在哪个块......

4. 清空"状态位图"(初始化 Bitmap)
把 Block Bitmap 和 Inode Bitmap 全部用 0 填满。

全是 0 代表什么?代表所有数据块都是空的,所有 inode 也都是空闲的。

5. 创建"根目录"
在 Inode Table 里分配 1 号 inode 给根目录 /,并在某个数据块里写入根目录的初始内容(通常是 . 和 ..)。

GDT(Group Descriptor Table)

cpp 复制代码
struct ext2_group_desc {
    __le32 bg_block_bitmap;      // 块位图在这个块组的哪个块?
    __le32 bg_inode_bitmap;      // inode位图在这个块组的哪个块?
    __le32 bg_inode_table;       // inode表在这个块组的哪个块?
    __le16 bg_free_blocks_count; // 这个块组还剩多少空闲块?
    __le16 bg_free_inodes_count; // 这个块组还剩多少空闲inode?
    // ...
};

详细解释:

因为一个大分区被切成了很多个 Block Group,GDT 就是用来管理这些 Block Group 的。它相当于一个"目录",告诉你 Block Group 0 的位图在哪、Block Group 1 的位图在哪。

协作情况:当系统要在某个组找 inode 时,先查 GDT,瞬间定位到那个组的 Inode Table 起始位置。

Block Bitmap(块位图)

内部结构 :

它根本不是一个复杂的结构体,它就是一段连续的字节(byte) 。每一个**比特位(bit)**代表一个数据块(Block)。

  • 0 代表该数据块是空闲的。

  • 1 代表该数据块已经被占用了。

详细解释 :

假设本块组的 Block Bitmap 是 0000 0001(从右往左看),说明 0号到6号块是空的,7号块被占了。

协作情况 :当你要往文件里写数据(比如追加 4KB),文件系统扫描这串 0101 的位图,找到第一个为 0 的 bit,把它改成 1,就说明这个块被分配给你了。不仅分配快,还省空间(1个字节就能管8个块)。

Inode Bitmap(inode 位图)

内部结构 :

和块位图一模一样,也是一串连续的字节。每一个**比特位(bit)**代表一个 inode。

  • 0 代表这个 inode 号码还没人用。

  • 1 代表这个 inode 已经被分配出去了。

详细解释 :

Inode Table 是一个大数组(比如能容纳 1024 个 inode),Inode Bitmap 就是用来标记这 1024 个 inode 哪些被用掉了。

协作情况 :当你 touch abc 新建文件时,系统查 Inode Bitmap,找到第一个为 0 的 bit,把它标记为 1,并给你分配这个 inode 号(比如 263466)。

Inode Table(索引节点表)

cpp 复制代码
struct ext2_inode {
    __le16 i_mode;        // 文件类型和权限 (比如 0644)
    __le16 i_uid;         // 所有者ID
    __le32 i_size;        // 文件大小(字节)
    __le32 i_atime;       // 访问时间
    __le32 i_mtime;       // 修改时间
    __le32 i_links_count; // 硬链接数
    __le32 i_block[15];   // 核心!记录文件数据存放在哪些 Block 里
    // ...
};

详细解释 :

这就是我们前面反复提的inode 。在 4KB 的数据块里,一个 128 字节的 inode 可以塞下 32 个。所以一个块组里的 Inode Table 可能占据了成百上千个数据块,里面密密麻麻全是 inode。

协作情况 :一旦分配了 inode 号,系统就会在 Inode Table 里找到这个结构体,把文件大小、权限、以及真正的数据存在哪几个块 (i_block 数组),全部记进去。

Data Blocks(数据块)

内部结构 :

没有特定结构,就是一块 4KB 的空白内存。存什么完全取决于你的文件。

  • 如果你存的是代码,这里面就是代码字符。

  • 如果你存的是目录,这里面存的就是 {文件名, inode号} 的列表(这就是前面说的"目录也是文件")。

协作情况 :Inode 里的 i_block[15] 记录着它们的编号。OS 拿着编号,直接去 Data Blocks 里读数据。

疑问

1、为什么super block单独放在Block group内部,而不是单独拿出来?

bash 复制代码
机械硬盘是极其脆弱的物理设备。如果一块磁道坏了,或者磁盘表面刮花了,那块区域的数据就彻底没了。

如果只放一份:假设你把 Super Block 单独放在分区的开头。一旦硬盘开头那几个扇区出现了物理坏道(这在早期硬盘中很常见),整个分区就直接瘫痪了,操作系统连这个分区有多大、有多少空闲空间都不知道,根本无法挂载。

冗余备份:Ext2 的设计者规定,每一个 Block Group 的开头都要包含一份 Super Block 的拷贝。而且,这些拷贝是保持同步的。

如果 Group 0 的 Super Block 所在的物理磁道坏了,操作系统会尝试去 Group 1 读取备份的 Super Block。

只要有一份备份存活,你的文件系统就还能被抢救回来。

(注:现代文件系统如 ext4 做了优化,虽然每个组都有备份空间,但为了节省空间,通常只在少数几个组里真正存储 Super Block 的完整拷贝,这叫稀疏备份。)

2、inode的编号在文件系统里面是唯一的吗

是的,inode 的编号在整个文件系统(也就是一个独立的分区)内是唯一的。

"inode和数据块,跨组编号的。所以,在同一个分区内部,inode编号和块号都是唯一的!"

  • inode 表是分开的 :Block Group 0 有自己的一段 Inode Table,Block Group 1 也有自己的一段 Inode Table。

  • 编号是全局递增的:虽然它们分散在不同的组里,但文件系统给它们统一编了号。

    • 比如:Group 0 的 inode 编号可能是 1 ~ 1024。

    • Group 1 的 inode 编号紧接其后,可能是 1025 ~ 2048。

  • 计算公式 :如果已知一个 inode 号,通过 (inode号 - 1) / 每组inode数,就能算出它在哪个块组;通过 (inode号 - 1) % 每组inode数,就能算出它是这个组里的第几个 inode。

怎么知道 inode 在哪一个分组里?

假设你在 Super Block 里查到:每个块组(Block Group)固定包含 8192 个 inode 。

现在你手里拿着一个文件的 inode 编号:12345。

操作系统用脚趾头算一下就知道了:

分组号 = (inode 编号 - 1) ÷ 每组 inode 数

分组号 = (12345 - 1) ÷ 8192 = 12344 ÷ 8192 = 1(取整)

结论 :这个 inode 12345 存在 Block Group 1 里面。

(注:减 1 是因为 inode 编号是从 1 开始的,而数组下标是从 0 开始的)

问题二:怎么知道在分组中位图的哪一个位置?

既然知道了它在 Group 1,那它在 Group 1 的 Inode Bitmap 里,具体是第几个比特位(bit)呢?

接着用刚才的余数算:

组内偏移量 = (inode 编号 - 1) % 每组 inode 数

组内偏移量 = 12344 % 8192 = 4152

结论 :它的确切位置是 Group 1 的 Inode Bitmap 里的第 4152 个 bit。

进一步定位到具体结构体:

  • Inode Bitmap 里 :第 4152 个 bit 肯定被标记为了 1(表示已分配)。

  • Inode Table 里:系统直接在 Group 1 的 Inode Table 中,往后跳过 4151 个 inode 结构体(每个 128 字节),找到第 4152 个。这个结构体,就是你要找的文件的属性档案!

相关推荐
天空属于哈夫克344 分钟前
企业微信API二次开发 赋能私域运营自动化
运维·自动化·企业微信
天远API1 小时前
零信任架构实战:基于天远运营商二要素V即时版构建自动化分时租赁出行会员身份一致性审核网关
运维·人工智能·架构·自动化
九零HTTP1 小时前
10月21号-CSDN配菜-IP池的5个坑
服务器·数据库·tcp/ip
SHENGWEI胜为1 小时前
交换机端口显示正常,终端却获取不到 IP 地址?从 DHCP 流程定位故障
服务器·网络·测试工具·缓存·通信工程
极光通讯2 小时前
液冷服务器对内存和SSD的适配要求:高密度部署下的散热与信号完整性
运维·服务器
2501_937767163 小时前
面向设计 / 研发部门:终端文件统一归档 + 服务器定时备份实践
运维·安全
GeW3 小时前
新质生产力路线图:从Linux到数据库,企业落地要点都在这
linux
IMPYLH3 小时前
Linux 系统备份与镜像
linux·服务器·网络
qetfw3 小时前
Bash 字符串处理:参数展开、序列与脚本执行方式
linux·开发语言·chrome·bash