从这一课起进入第五阶段:存储与 I/O。前面讲的内存是"电"里存的东西,一断电就没了;而你的文件,是要持久地存在磁盘上、关了机还能找回来的。那磁盘上一堆裸字节,是怎么被组织成你看到的"文件、文件夹"的?"文件名"和"文件"是同一个东西吗?这一课,我们走进文件系统

从"文件名其实是个假象"说起

你在文件管理器里看到的是一棵漂亮的目录树:/home/user/文档/笔记.md。但磁盘上根本不存在"树"——磁盘就是一大片连续的字节。

那这个树状结构是谁"造"出来的?答案是文件系统:它用一套规则,把磁盘上那些裸字节,组织成"文件/目录"这种人类能懂的结构,并记录每个文件的数据放在哪、多大、归谁。

这一课,我们就拆开这套规则看。


19.1 文件系统解决什么问题

一句话理解:裸磁盘是一堆字节,文件系统负责把它们组织成"文件/目录"这种人类能懂的结构,并记录每个文件的数据放在哪、多大、谁拥有。

文件系统要回答三个基本问题:

  1. 数据放在哪:一个文件的内容,具体落在磁盘的哪些块上?
  2. 元信息是什么:文件多大、什么权限、谁拥有、什么时候改的?
  3. 怎么组织成树:目录和文件怎么关联,形成你看到的层级?
一句话:文件系统 = 数据块(存内容)+ 元数据(存描述)+ 目录(存层级关系)。

19.2 inode——文件的"身份证"

这是文件系统里最重要的一个概念,必须吃透:

一句话理解文件名不是文件的本质,inode 才是。 一个 inode 存文件的所有元信息(大小、权限、拥有者、时间戳、数据块位置),文件名只是指向 inode 的一个"标签"。

很多人以为"文件 = 文件名 + 内容"。其实错了。真正的结构是:

  • inode:存这个文件的一切元信息——大小、权限、拥有者、三个时间戳(atime 访问时间 / mtime 修改时间 / ctime 元数据变更时间)、数据块的位置
  • 文件名:只是一个指向 inode 的标签,存在目录里。

一个惊人的推论:一个 inode 可以有多个文件名。你在一个 inode 上挂两个名字,这两个名字指向同一个文件——这就是硬链接(hard link)

生活类比:inode 是一个"人",文件名是"称呼"。你可以有好几个昵称(硬链接),但本质上是同一个人。删掉一个昵称,人还在;只有当所有昵称都删光、引用计数归零,这个人(inode)才真正消失。

19.3 目录是什么

一句话理解:目录也是一个文件,内容是一张表:文件名 → inode 号。所以"打开文件" = 从目录查 inode → 从 inode 找数据块。

目录不是什么特殊东西,它也是一个文件,只不过内容是一张对照表:

文件名          inode 号
a.txt           12345
b.txt           67890

所以你要打开 /home/user/a.txt,过程是:

  1. 从根目录 / 查到 home 的 inode;
  2. home 的 inode 查到 user 的 inode;
  3. user 目录里查到 a.txt 对应的 inode 号;
  4. 从那个 inode 里,找到 a.txt 的数据块位置,读出来。
一句话:路径是一层层"查目录表"的过程。这也解释了为什么"目录里不能有两个同名文件"——因为目录表里 文件名 → inode 是唯一的。

19.4 分配方式:数据块怎么放

一个文件的内容,在磁盘上怎么放?三种经典方式:

方式做法优点缺点
连续分配文件占连续磁盘块顺序读极快有外碎片、文件难增长
链式分配每块存"下一块指针"无外碎片随机访问要顺着链走,慢
索引分配inode 存一张"块指针表"随机访问快、无外碎片表本身要空间
  • 连续分配:像磁带,文件内容连续排在一起。顺序读极快,但文件要增长时麻烦(后面没地方了),还会产生外部碎片
  • 链式分配:像链表,每块末尾存"下一块在哪"。没有外碎片,但要读第 100 块,得顺着链走 99 步,随机访问慢
  • 索引分配:inode 里存一张"块指针表",直接记录每个数据块在哪。要读第 100 块,直接查表第 100 项 → 随机访问快,也无外碎片。缺点是要占空间存这张表。
一句话理解:现代文件系统多用索引分配(inode 里存数据块指针,大文件用多级间接块)。

ext4 的做法:inode 里有 12 个直接指针(直接指向数据块,够小文件用)+ 一级/二级/三级间接指针(指向"指向数据块的块",给大文件用)。小文件直接用 12 个直接指针就够,快;超大文件用多级间接,能覆盖巨大空间。


19.5 FAT vs ext4

两种典型文件系统对比:

FAText4
组织文件分配表(链式)inode + 块组(索引)
大文件/性能一般好(extent 连续块)
日志有(journal)
适用U 盘、嵌入式、跨系统Linux 主力
  • FAT:老牌的"文件分配表"方案,本质是链式分配。因为跨平台兼容性好,至今还在 U 盘、相机存储卡里用;
  • ext4:Linux 的主力,用 inode + 块组(索引分配),配合 extent(连续区段),大文件性能好,还有日志(下一节)。
一句话:FAT 胜在通用,ext4 胜在性能和可靠性。

19.6 日志(journaling)为什么重要

最后一个关键点:日志

一句话理解:写文件往往要改多个地方(inode、数据块、目录),中途断电会"改一半"。日志先把"要做什么"记下来,崩溃后能回放或回滚,保证一致性。

问题:写一个文件,可能要同时改 inode、数据块、目录表 好几个地方。如果改到一半突然断电,就会出现"改了一半"的烂摊子——比如数据写了,但 inode 没更新,文件就"丢"了。

日志的解法:

  1. 写之前,先把"我要做什么"记进日志区
  2. 再动手改真正的数据;
  3. 如果中途崩溃,重启时检查日志:没写完的回滚,写完了但没生效的回放,保证文件系统始终处于一致状态
所以 ext4 默认开日志,突然断电后重启,系统能自动恢复,很少出现文件系统损坏。这就是"日志"的价值——把"改一半"的风险,用"先记账"来化解

动手实验:命令观察 inode 和文件占用

touch demo.txt
echo "hello os" > demo.txt
ls -i demo.txt        # 看 inode 号
stat demo.txt         # 看 inode 详情:大小、块数、时间戳、权限
df -h                 # 看文件系统总容量/已用
df -i                 # 看 inode 用量(小文件多会耗尽 inode)
ln demo.txt demo_hard.txt   # 硬链接:两个名字同一个 inode
ls -i demo.txt demo_hard.txt  # inode 号相同

重点观察:

  • ls -i 显示的 inode 号
  • stat 里的 Blocks(占用块数,常是 8 块——因为最小分配单位是 4KB,一个文件至少占一个块,stat 按 512B 扇区计,所以显示 8)、Inode 号、三个时间戳(atime/mtime/ctime);
  • 建立硬链接后,demo.txtdemo_hard.txtinode 号完全相同——验证了"两个名字指向同一个 inode"。

深入点:两个进阶话题

① extent(区段)

ext4 的"索引分配"里,其实不是逐个块存指针,而是用 extent

用"起始块 + 长度"描述一段连续块,代替逐个块指针。一个大文件可能只需几条 extent 就描述完了,指针表小、效率高。

② inode 耗尽

一个经典坑:磁盘明明还有空间,却报 "No space left on device"?

可能不是容量满了,而是 inode 用光了——比如目录里塞了海量小文件,每个文件都要一个 inode,inode 先被耗尽。df -i 能看出来(inode 使用率 100% 而容量还有剩)。

小结与思考题

这一课,我们拆开了文件系统的内核:

  • inode 是文件的本质:存元信息和数据块位置;文件名只是指向它的标签;
  • 目录 = 一张"文件名→inode"表:打开文件就是一层层查表;
  • 三种分配方式:连续(快但碎片)、链式(无碎片但慢)、索引(随机快,现代主流);
  • 硬链接:多个名字指向同一个 inode;
  • 日志:先记账再动手,崩溃后能回放/回滚,保证一致性。

留三个问题:

  1. 文件名和 inode 是什么关系?硬链接的本质是什么?
  2. 连续分配、链式分配、索引分配各自优缺点?
  3. 文件系统的"日志"解决什么问题?
文件是怎么存进磁盘的,我们搞懂了。但还有一个问题没问:磁盘这种"外设",到底是怎么跟 CPU 对话的? 你敲键盘、读硬盘,中间经过了什么?是 CPU 一直盯着它,还是它有消息了"喊"CPU 一声?下一课,我们走进 I/O 与设备,看清外设和 CPU 之间的三种对话方式。

标签: 计算机基础, 操作系统, Linux

添加新评论