Linux基础:Ext系列文件系统

文章目录

从磁盘到inode:一口气搞懂Linux Ext系列文件系统(小白友好完整版)

很多刚接触Linux的同学,一看到ext2/ext3/ext4、inode、块组、软硬链接这些名词就头皮发麻,总觉得文件系统是底层玄学,看不见摸不着。

其实这玩意儿一点都不玄,本质上就是给硬盘做「物业管理」------怎么存数据、怎么找数据、怎么管空间。今天咱们就从一块机械硬盘开始,一层一层往下扒,保证看完你能对着磁盘分区侃侃而谈,吃透底层缓存、删除机制、进程与文件交互逻辑。

一、先搞懂硬盘本身:数据存在哪?

1.1 机械硬盘的物理结构

机械硬盘是电脑里少有的纯机械设备,靠转圈干活。你可以把它想象成一叠黑胶唱片:

  • 盘片(Platter):就是那张「唱片」,两面都能存数据,每个盘面对应一个磁头
  • 磁道(Track):盘面上一圈一圈的同心圆纹路,数据就存在这些纹路里,从外圈往里圈编号
  • 扇区(Sector) :把磁道切成一小块一小块的扇形,这是磁盘读写的最小物理单位,传统大小512字节
  • 柱面(Cylinder):所有盘片上半径相同的磁道,叠起来就像一个空心圆柱。所有磁头是「共进退」的,同一时间都在同一个柱面上

一句话总结:硬盘读写数据,就是磁头定位到某个柱面,选中某个磁头,然后等扇区转过来,读写数据。

1.2 怎么找到数据?CHS与LBA寻址

早期的硬盘,找数据靠CHS寻址 :C=柱面号,H=磁头号,S=扇区号。就像你收快递写地址:几栋(柱面)几单元(磁头)几号房(扇区)。

但CHS有个致命问题:容量上限只有8GB左右(256磁头 × 1024柱面 × 63扇区 × 512字节),硬盘越做越大,这地址根本不够用。

于是就有了LBA(逻辑块地址) :

把整个硬盘所有扇区按顺序排成一个一维数组,每个扇区有一个编号(从0开始),这就是LBA地址。

操作系统只需要告诉硬盘「我要读LBA=10086的扇区」,硬盘内部的固件自己把LBA转换成CHS去定位。

简单说:CHS是物理地址,LBA是逻辑地址。现在我们基本只跟LBA打交道,CHS交给硬盘自己玩。

小计算:1个块4KB=8个扇区,所以块号 = LBA号 / 8,反过来LBA = 块号 × 8 + 块内偏移。

二、文件系统是干嘛的?给硬盘做「物业管理」

直接往LBA扇区里写数据不行吗?行,但你写了之后自己都找不到在哪。文件系统就是给硬盘做空间管理、目录索引的「物业系统」。

2.1 先搞懂两个基础概念:块 & 分区

(1)块(Block):文件读写的最小单位

扇区512字节太小了,操作系统一次只读一个扇区效率太低,就像你买米一粒一粒买。所以文件系统把连续的8个扇区(4KB)打包成一个块 ,文件读写的最小单位是块。

块大小在格式化的时候确定,最常见的就是4KB。

(2)分区(Partition):给硬盘「隔房间」

一块大硬盘,我们会把它分成几个区,就像一套大房子隔成卧室、客厅、书房。分区的本质就是划定起止LBA范围,每个分区独立管理,互不干扰。

Linux里一切皆文件,分区也是文件,比如/dev/sda1就是第一块硬盘的第一个分区。

三、灵魂概念:inode是什么?

这是Linux文件系统最核心的概念,很多人学了很久都没搞透。

3.1 文件 = 内容 + 属性

一个文件,比如test.txt,包含两部分:

  • 内容数据:文件里写的文字、代码、视频数据
  • 属性数据:文件大小、权限、所有者、创建/修改时间、占用多少块

Linux的设计哲学是:属性和内容分开存 。

内容存在数据块里,而属性,就存在一个叫inode(索引节点) 的东西里。

3.2 inode里有什么?

每个文件对应一个inode,inode里存了文件的所有元数据,我们用stat命令就能看到:

bash 复制代码
$ stat test.c
  File: test.c
  Size: 654       	Blocks: 8          IO Block: 4096   regular file
Device: 802h/2050d	Inode: 263715      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2017-09-13 14:56:57.059012944 +0800
Modify: 2017-09-13 14:56:40.067012944 +0800
Change: 2017-09-13 14:56:40.069012948 +0800

内核里ext2_inode结构体的核心字段:

  • i_mode:文件类型 + 权限位
  • i_uid/i_gid:所有者用户ID / 组ID
  • i_size:文件字节大小
  • i_atime/i_mtime/i_ctime:访问时间 / 内容修改时间 / 属性变更时间
  • i_links_count:硬链接计数(磁盘上有多少文件名指向该inode)
  • i_block[15]:数据块指针数组,指向文件内容所在的数据块

划重点:inode里不存文件名!

很多人第一次知道都惊了:合着文件名就是个马甲?没错!文件名根本不算文件的固有属性,它只是方便我们人类记忆的「别名」。

3.3 文件名存在哪?目录文件里!

目录也是文件!只不过目录文件的内容,是一张映射表 :里面存着该目录下所有文件名和对应inode号的对应关系。

目录文件在数据块里,下方有解释

你可以把目录理解成一个「通讯录」:

  • 左边是人名(文件名)
  • 右边是身份证号(inode号)

你想找文件,先查通讯录拿到身份证号(inode号),再凭号去inode表找文件信息,最后去数据块拿内容。

用ls -i就能看到文件名对应的inode号:

bash 复制代码
$ ls -li
1052007 -rw-rw-r-- 1 whb whb 488 Oct 17 19:06 main.c
1052669 -rw-rw-r-- 1 whb whb 225 Oct 17 19:09 test.c

四、Ext2文件系统全景:块组是怎么回事?

ext2是Ext系列的经典款,ext3/ext4都是在它基础上升级的,核心结构没变。

ext2不会把整个分区一锅粥管理,而是把分区分成一个个块组(Block Group),每个块组独立管理自己的空间,就像小区分成好几栋楼,每栋楼自己管自己的住户。

每个块组从前往后依次是这几部分:

1. 超级块(Super Block)

整个文件系统的「总账本」,记录整个分区的全局信息:总块数、总inode数、空闲块/空闲inode数量、块大小、inode大小、挂载时间等等。

超级块非常重要,坏了整个文件系统就崩了,所以它会在多个块组里做冗余备份。

2. 块组描述符表(GDT)

每个块组的「档案袋」,记录这个块组的元信息:块位图在哪、inode位图在哪、inode表在哪、空闲块/空闲inode还有多少。

3. 块位图(Block Bitmap)

一张「入住登记表」,每个比特位对应一个数据块:0表示空闲,1表示已占用。

想分配新的数据块?查位图找0的位置,标记成1就行,效率极高。

4. inode位图(Inode Bitmap)

和块位图同理,每个比特位对应一个inode:0空闲,1已占用。

5. inode表(Inode Table)

存放这个块组里所有的inode,每个inode大小固定(一般128字节或256字节)。inode号是分区内唯一的,通过inode号就能算出它在哪个块组的inode表里。

6. 数据块(Data Blocks)

真正存文件内容的地方。普通文件存内容,目录文件存文件名‑inode映射表。

一个形象的类比:

整个分区 = 一个小区

块组 = 一栋楼

超级块 = 小区物业总台账

块位图 = 每户入住状态表

inode表 = 住户档案

数据块 = 住户家里的储物空间

目录 = 楼层通讯录

补充:inode怎么找到数据块?

inode里有一个i_block[15]数组,一共15个指针,采用「直接+多级间接」的设计:

  • 12个直接指针:直接指向数据块。4KB块的话,48KB以内的小文件,直接通过这12个指针就能找到所有内容,速度极快。
  • 1个一级间接指针:指向一个索引块,索引块里存的全是数据块指针。4KB块、每个指针4字节的话,一个索引块能存1024个指针,对应4MB文件。
  • 1个二级间接指针:两层索引,能对应4GB左右的文件。
  • 1个三级间接指针:三层索引,支持超大文件。

这就是为什么Linux既能高效处理小文件,也能支持大文件。

五、路径解析、内核缓存与打开文件完整流程

你以为cat /home/whb/test.txt很简单?内核在背后做了一整套流程。

5.1 磁盘层面原始路径解析(无缓存)

  1. 从根目录出发 :根目录/的inode号是固定的,系统开机就知道。
  2. 找home目录 :读取根目录的数据块(目录内容),找到home对应的inode号。
  3. 找whb目录 :读取home目录的数据块,找到whb对应的inode号。
  4. 找test.txt :读取whb目录的数据块,找到test.txt对应的inode号。
  5. 读文件内容:拿到test.txt的inode,根据里面的块指针,去数据块读取文件内容。

这个一层一层磁盘读取查找的过程,就叫路径解析。如果每次访问都读磁盘,性能极差,所以内核引入两套核心缓存。

5.2 dentry缓存(dcache目录项缓存)

⚠重点区分:磁盘上有「目录项」;内存里的结构体叫 dentry。磁盘没有dentry,dentry是内核内存对象。

每次磁盘解析目录得到的(父目录+文件名 → inode)结果,内核封装为dentry,全部放入全局内核缓存dcache。

  • dentry核心成员:d_name(单级文件名,不存完整路径),d_parent(父dentry指针),d_inode(指向内存inode缓存)
  • dentry在内存形成一颗完整目录树,全部进程共享。进程退出不会清理dentry;只有内存不足、文件删除重命名、umount卸载分区才会回收失效。

有缓存时打开文件:

直接沿着dentry父子指针在内存遍历匹配文件名,不需要读取磁盘目录块,速度提升几个数量级。缓存缺失才回落去读磁盘构造新dentry。

重要限制:dcache是正向映射:父dentry +文件名 → inode。不提供反向索引,不能通过inode号反查文件名。一个inode对应多个硬链接时,会对应多条独立dentry。

5.3 inode缓存

从磁盘inode表读出的inode元数据,拷贝一份到内存,就是inode缓存。以inode号作为key,命中缓存就不用读取磁盘上的inode表。

dcache拿到dentry之后,通过d_inode指针直接拿到内存inode缓存。

5.4 进程打开文件:fd、file结构体,两套引用计数

当进程open成功之后:

  1. 内核生成struct file内存对象,存放文件偏移、读写标志;里面维护f_count,内存打开引用计数,统计多少处正在打开使用该文件。
  2. 在进程私有的文件描述符表分配fd(整数),fd指向这个struct file。

两套计数一定要分清:

  1. i_links_count(磁盘inode):统计磁盘上有多少文件名(硬链接)指向inode;为0代表已经从目录树上摘除。
  2. f_count(内存struct file):统计多少进程打开持有该文件。

磁盘空间释放的双重条件,两个计数全部归0:

  1. i_links_count == 0:磁盘已经没有任何文件名指向这个inode
  2. f_count == 0:没有任何进程打开这个文件

现象:rm删除文件之后磁盘空间不释放。原因就是i_links_count已经为0,但还有进程打开文件,f_count不为0,磁盘数据块与inode暂时保留;所有进程close之后才真正回收磁盘空间。

rm本质就是unlink系统调用:删除目录里的文件名记录,i_links_count--,不一定立刻删磁盘数据。

5.5 软链接打开的特殊逻辑

如果dentry拿到inode,检测到文件类型是符号链接l:

不会直接使用当前inode对应的内容;读取软链接inode内部存储的路径字符串,丢弃当前查找结果,重新完整执行一轮路径解析流程 。

软链接拥有自己独立inode,完全不修改原文件的i_links_count硬链接计数。

六、挂载:分区怎么和目录挂钩?

分区格式化好了,里面有完整的文件系统,但你直接是访问不到的。必须把它挂载(mount) 到一个目录上,这个目录就叫「挂载点」。

举个实操例子:

bash 复制代码
# 制作一个5MB的镜像文件,模拟一块磁盘
$ dd if=/dev/zero of=./disk.img bs=1M count=5
# 格式化为ext4文件系统
$ mkfs.ext4 disk.img
# 创建空目录作为挂载点
$ mkdir /mnt/mydisk
# 将镜像挂载到目录上
$ sudo mount -t ext4 ./disk.img /mnt/mydisk/
# 查看挂载情况
$ df -h
# 用完卸载
$ sudo umount /mnt/mydisk

挂载的本质:给这个文件系统找一个目录入口 。以后你访问/mnt/mydisk,其实就是在访问这个分区里的内容。

Linux的目录树是统一的,所有分区都挂载到这棵树上的某个节点,这和Windows的C盘D盘完全不同。

小知识:上面例子里用文件当块设备挂载,用到了loop设备(回环设备),它可以把普通文件模拟成块设备,挂载ISO镜像就是这个原理。

umount卸载的时候,该文件系统全部dcache、inode缓存会被清空。

七、软硬链接:两个文件指向同一份数据?

7.1 硬链接(Hard Link)

硬链接就是同一个inode,多个文件名。相当于给同一个文件起了好几个别名。

bash 复制代码
# 创建硬链接:ln 源文件 链接文件
$ ln abc def
$ ls -li abc def
263466 -rw-r--r-- 2 root root 0 9月 15 17:45 abc
263466 -rw-r--r-- 2 root root 0 9月 15 17:45 def

可以看到两个文件inode号完全一样,硬链接数变成了2。

删除硬链接文件的时候:

  1. 在目录里删掉这条文件名记录
  2. inode里的硬链接数i_links_count减1
  3. 只有当硬链接数减到0,并且没有进程打开文件时,这个inode和对应的数据块才会被真正释放

硬链接的特点:

  • 不占用新的inode,只是新增一条目录项
  • 删除任意一个硬链接文件,只要链接计数大于0,文件本身还在
  • 不能跨分区(inode号仅分区内唯一)
  • 默认不能给目录创建硬链接(.和..是系统自动创建的目录硬链接)
    如果给父目录创建硬链接,系统无法自动返回上一级目录,会将父目录当做当前目录的子目录,造成一个循环卡死

7.2 软链接(Symbolic Link)

软链接就是Windows里的快捷方式。它是一个独立的文件,有自己的inode,文件内容里存的是目标文件的路径字符串。

bash 复制代码
# 创建软链接:ln -s 源文件 链接文件
$ ln -s abc abc.s
$ ls -li
263563 -rw-r--r-- 2 root root 0 9月 15 17:45 abc
261678 lrwxrwxrwx 1 root root 3 9月 15 17:53 abc.s -> abc

可以看到软链接有自己独立的inode号,文件类型是l(链接文件)。

如果把原文件abc删了,软链接abc.s就会变成「悬空链接」,指向一个不存在的文件。

软链接的特点:

  • 是独立文件,有自己的inode和数据块
  • 可以跨分区、跨文件系统
  • 可以给目录创建软链接
  • 原文件删除后,软链接失效,不会影响原文件硬链接计数

软硬链接对比表

特性 硬链接 软链接
inode号 和原文件相同 独立inode
文件类型 和原文件一致 链接文件(l)
跨分区 不可以 可以
支持目录 不支持(除.和...) 支持
删除原文件 不受影响,链接数‑1 失效,变成悬空链接
占用空间 几乎不占(仅目录项) 占一个inode和数据块

八、Ext家族进化:ext3、ext4强在哪?

Ext3:加了日志的ext2

ext3和ext2结构几乎完全一样,最大的升级就是加了日志(Journal)功能 。

没有日志的ext2,突然断电可能导致文件系统不一致,开机要全盘扫描检查,大硬盘要扫几十分钟。

ext3在写数据之前,先把「我要做什么操作」写到日志里,写完数据再更新日志。万一断电了,开机只需要看日志,把没完成的操作回滚/重做就行,恢复速度极快。

ext3有三种日志模式:

  • 写回模式(Writeback):只记录元数据日志,数据不记,性能最好,安全性一般
  • 顺序模式(Ordered):默认模式,先写数据,再写元数据日志,平衡性能和安全
  • 数据模式(Journal):数据和元数据都写日志,最安全,性能最差

Ext4:全面升级的下一代

ext4在ext3基础上做了大量优化,是现在大多数Linux发行版的默认文件系统:

  • 更大的容量:支持最大1EB的分区,最大16TB的单个文件
  • 延迟分配:先攒一攒再分配磁盘块,减少碎片,提升性能
  • 多块分配:一次分配多个连续块,提升大文件写入速度
  • 纳米级时间戳:时间精度从秒级提升到纳秒级
  • 日志校验:日志增加校验和,更可靠
  • 在线碎片整理:挂载状态下也能整理碎片

九、完整逻辑总结

  1. 物理硬盘由盘片、磁头、扇区组成,现代使用LBA一维逻辑地址寻址。
  2. 文件系统把扇区打包成块;磁盘划分分区,每个分区内部切割成多个块组进行管理。
  3. 文件元数据放在inode,实际内容放在数据块;文件名保存在目录文件的映射表,inode不存储文件名。
  4. 访问文件需要从根目录逐层做路径解析;dcache(dentry)、inode缓存是内核全局缓存,极大减少磁盘IO。dentry是内存对象,磁盘只有目录项。
  5. 文件删除有两套引用计数:磁盘i_links_count硬链接计数,内存f_count打开计数;两套计数全部归零才真正释放磁盘空间。
  6. 硬链接:多文件名共享同一个inode;软链接是独立inode,内部存放目标路径字符串,打开时二次路径解析。
  7. Linux所有分区必须mount挂载到目录树的挂载点才可以访问,和Windows盘符模型完全不同。

搞懂了这些,你再看ls、stat、ln、mount、rm这些命令,就知道它们背后到底在操作什么了。文件系统不是玄学,就是一套精心设计的「数据管理方案」。

相关推荐
沫璃染墨8 分钟前
《从零入门Linux系统篇(五十一):线程篇·四——pthread线程库详解:从线程创建到终止与分离》
linux·运维·服务器·开发语言·c++·系统架构·线程
YYRAN_ZZU10 分钟前
GMSL开发加解串配置介绍
linux·嵌入式硬件
墨家前任巨子19 分钟前
定时关机脚本
运维
云计算练习生19 分钟前
什么是上下文切换?进程切换时 CPU 到底在保存什么
linux·服务器·操作系统·进程切换
用户05101225729620 分钟前
RGA(二)——RK3588 的核拓扑与能力差异
linux·嵌入式
ESDWAN21 分钟前
企业组网后网络频繁掉线,常见故障排查指南
运维·网络·架构
用户05101225729622 分钟前
RGA(一)——基础知识
linux·嵌入式
qetfw25 分钟前
Debian 综合服务实训:Apache2、Samba、LDAP、vsftpd 与 OpenVPN
linux·服务器·debian
用户05101225729626 分钟前
RGA(三)——实宽高、虚宽高与对齐约束
linux·嵌入式
fastjson_36 分钟前
帆软看板 - 问题收集
linux·前端·javascript