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盘符模型完全不同。

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

相关推荐
qq_349447951 小时前
Linux系统,安装git,从使用git下载仓库(GitHub, Gitee, GitLab 等),并且使用ssh密钥,可以直接执行git pull
linux·git·ssh
GlobalSign数字证书4 小时前
小企业零运维 TLS 架构设计:证书生命周期管理(CLM)与自动化轮换策略的技术选型
运维·自动化
mCell4 小时前
GitHub Actions 玩法大赏:一台远程主机的七种活法
linux·github·agent
盐焗鹌鹑蛋4 小时前
【Linux】动静态库的制作与使用
linux
wuyk5555 小时前
Python 第三章:列表 List
服务器·开发语言·python
艾伦_耶格宇5 小时前
【ELK】-4 logstash的搭建及操作
linux·运维·ubuntu·elk
mifengxing6 小时前
文件逻辑结构、物理结构与磁盘空闲存储空间管理
大数据·linux·运维·操作系统·计算机408
听你说326 小时前
智亮苍南・绿动未来!中节能晶和科技打造浙南县域智慧照明示范样板
大数据·运维·人工智能·科技