Skip to content

文件的物理结构

2026 大纲 四(一)6 文件的物理结构

文件内第 8000 个字节,到底在盘上哪儿

上一节把逻辑结构讲完了,它的产出是一个数:文件内偏移量。 这一节要把这个数变成一个物理位置——第 8000 个字节在哪个盘块的第几个字节

块内偏移好办,除一下就有了。真正的问题是前一半:文件的第 i 个逻辑块, 对应磁盘上的哪个物理块?

这是一条映射。而三种分配方式的全部分歧,就在于把这条映射存在哪里

  • 存在目录项里——但那儿只放得下一个数,所以只能要求文件块在盘上连着放, 第 i= 起始块号 +i,一个加法搞定。这是连续分配
  • 存在数据块自己里——每块末尾留个指针指向下一块。这是隐式链接
  • 抽出来集中存——要么全盘一张大表(FAT),要么每个文件一张小表(索引分配)。

这一个选择推出后面的全部优缺点,两条判据就够用:

支不支持随机存取,看"能不能不读磁盘就算出第 i 块的块号"。 连续分配算得出(加法)、FAT 查得到(表常驻内存)、索引分配读一次索引块就拿到全部块号—— 只有隐式链接不行,因为不把当前块读回来就不知道下一块是谁。

有没有外部碎片,看"分配是否要求物理上连成一片"。只有连续分配要求,所以也只有它有外部碎片,也只有它"必须预知文件大小"、 "增长要整体搬家"——三件事同源。

这一节是 file 章出题最密的一处,而且大题几乎年年落在这里。 后半部分的混合索引计算要练到熟,它的每一步都是固定动作。

一、连续分配

磁盘被划分为大小相等的磁盘块(block),物理结构要解决的就是如何把文件数据分配到这些块上

连续分配让每个文件占用一组连续的磁盘块,目录项记录起始块号和长度——像在书架上给一本书留一整排连续格子:读起来最快,但中间插不进新书。访问第 i 个块直接算 起始块号 + i

目录项: {文件名: "data.txt", 起始块: 5, 长度: 4}

磁盘块: ... | 5 | 6 | 7 | 8 | ...
              └── data.txt ──┘
加载可视化中...
优点缺点
支持顺序存取和随机存取产生外部碎片
顺序读写速度最快(磁头不需要移动)文件不能动态增长
实现简单,只需起始块号+长度必须预先知道文件大小
外部碎片是怎么一步步长出来的:一串建删动作在 20 块磁盘上的逐步演化(想看清"总量够却放不下"是怎么发生的时展开)

碎片不是一次分配造成的,是删除与重建反复交替攒出来的。设磁盘有 20 个块,初始全空,依次执行:

步骤动作磁盘状态(. 空闲)最大连续空闲段
建 A(5)、B(4)、C(6)、D(3)AAAAABBBBCCCCCCDDD..2
删 BAAAAA....CCCCCCDDD..4
删 DAAAAA....CCCCCC.....5
建 E(3)(首次适应,放进 B 留下的 4 块洞)AAAAAEEE.CCCCCC.....5
建 F(2)(前面只剩 1 块的洞,装不下,只能放到尾部)AAAAAEEE.CCCCCCFF...3

到第 ⑤ 步,磁盘上空闲块共 205362=4 块,但它们分成 1 + 3 两段。此时要建一个 4 块的文件:空闲总量正好够,却放不下。即便再把 F 删掉,空闲变成 6 块,也仍然是 1 + 5 两段,建不了 6 块的文件——空间明明够,就是连不起来。

三条推论直接从这里出来:

  1. 只有"要求连成一片"的分配方式才有外部碎片。 链接分配和索引分配按单块离散分配,任何一个空闲块都能用上。
  2. 首次适应/最佳适应这些策略只能减缓、不能消除。 它们改变的是洞怎么被挑选,改变不了"删除会把连续区域打断"这件事。
  3. 唯一的根治办法是紧凑(compaction)——把所有文件搬到磁盘一端合并空洞,代价是把磁盘上大量数据搬一遍。

二、链接分配

隐式链接

每个磁盘块中保留一个指针域指向下一块,目录项记录起始块和结束块——像寻宝游戏,每个线索只告诉你下一站在哪。

目录项: {文件名: "log.txt", 起始块: 2, 结束块: 10}

  块2        块5        块8        块10
┌─────┬──┐ ┌─────┬──┐ ┌─────┬──┐ ┌─────┬────┐
│数据 │→5│→│数据 │→8│→│数据│→10│→│数据 │null│
└─────┴──┘ └─────┴──┘ └─────┴──┘ └─────┴────┘
优点缺点
无外部碎片只能顺序存取,不支持随机存取
文件可动态增长指针占用空间
无需预知文件大小指针损坏会导致后续全部数据丢失

显式链接(FAT)

将所有磁盘块的链接指针集中存放在一张文件分配表(FAT, File Allocation Table)中,FAT 常驻内存。

FAT 表:
磁盘块号:  0    1    2    3    4    5    6    7    8  ...
指针值:   -1   -1    5   -1   -1    8   -1   -1   10 ...
                     ↓              ↓              ↓
            起始块2 → 块5 → 块8 → 块10(结束)
优点缺点
支持随机存取(在内存中的 FAT 中查找)FAT 需要占用内存空间
检索速度比隐式链接快得多磁盘块越多,FAT 越大
加载可视化中...
为什么要把指针从数据块里抽出来,以及 FAT 到底要占多少内存(想看清 FAT 为什么撑不到大容量盘时展开)

隐式链接把指针塞在数据块内部还有一个隐蔽后果:块内被指针占走几个字节后,每块可用的数据字节数不再是 2 的幂,"第 offset 字节在第几块"就只能真做除法而不能用移位。很多文件系统因此宁可把指针单独抽出来集中存放,保持数据块整块可用——这就是 FAT。

设磁盘容量 1 TB,块大小 4 KB,每个 FAT 表项占 4 B

第 1 步:把容量换算成块数。

块数=1 TB4 KB=240212=228=268435456  (256M 块)

FAT 是每个磁盘块一个表项的数组,表项数完全由块数决定、与文件多少无关。这一点是 FAT 与索引分配最大的结构差别——索引块只为已用的块建项,FAT 为全盘每一块都建项。

第 2 步:乘以表项宽度。

FAT 大小=228×4 B=230 B=1 GB

1 GB 的表要常驻内存——这才是 FAT 真正的边界:它不是"占点内存",而是随磁盘容量线性增长,最终大到无法常驻。

第 3 步:把块开大一档再算一次。 块大小改为 32 KB:

240215=225=33554432 ,225×4 B=227 B=128 MB

块变大 8 倍 → 块数变为 18 → FAT 也变为 18这就是 FAT 文件系统在大容量盘上必须用很大簇的原因,代价是每个文件平均浪费半个块的内部碎片——盘越大,簇越大,小文件越亏。

第 4 步:对比位示图。 同一块盘若改用位示图管理空闲空间(每块 1 位):

位示图=228 bit=225 B=32 MB

两者都是"每块一项"的全盘数组,差别只在每项几位——FAT 每项 4 B = 32 bit,位示图每项 1 bit,正好相差 32 倍。但它们回答的问题不同:位示图只回答"这块空不空",FAT 还要回答"这块的下一块是谁",所以位示图不能替代 FAT。

三、索引分配

为每个文件建立一个索引块,索引块中存放该文件所有磁盘块的块号。

目录项: {文件名: "code.c", 索引块: 15}

  索引块15
┌────────┐
│  块号2  │ → 数据块2
│  块号8  │ → 数据块8
│  块号5  │ → 数据块5
│  块号12 │ → 数据块12
└────────┘
优点缺点
支持随机存取索引块本身占用磁盘空间
无外部碎片小文件也需要一个索引块(浪费)
文件可动态增长大文件需要多级索引
加载可视化中...
小文件到底被这一块惩罚得多厉害:四档文件大小的空间利用率(想看清"开销固定一块"的量级时展开)

块大小 4 KB、块号占 4 B:

文件实际大小数据块索引块实占块数空间利用率
1 KB1121/8=12.5%
4 KB1124/8=50%
40 KB1011140/4491%
4 MB10241102599.9%

一个 1 KB 的文件要占掉 8 KB 磁盘(1 个数据块 + 1 个索引块,各自还有内部碎片),而索引块里 1024 个表项只用掉 1 个(4 B),其余 1023 个表项、共 4092 字节全是空的。由此得到一条判断:索引分配的开销与文件大小无关,是固定的一块,文件越小这一块占比越高——混合索引正是为了消掉它。

大文件的索引组织

一个索引块装不下所有块号时有三种方案。下面统一取块大小 4 KB、块号占 4 B,一个索引块可存 4096÷4=1024 个块号。

① 链接索引:多个索引块用指针串联,最后一个表项指向下一个索引块。

② 多级索引:类似多级页表,用索引块指向索引块。

索引级数可寻址的最大文件
一级1024×4KB=4MB
二级10242×4KB=4GB
三级10243×4KB=4TB

③ 混合索引(Unix 方案):inode 中同时使用直接地址项与一~三次间接地址项。

为什么要留一批直接指针,而不是一上来就用间接?因为绝大多数文件很小:直接地址项让"不超过 12 块(48 KB)"这一类文件在 inode 读进内存后一次索引块都不用读,访问任意一块都只要 1 次磁盘 I/O。这就是"为常见情形优化,为罕见情形保底"。

四部分寻址的是互不重叠的文件区间,所以最大文件大小是它们的和:4 KB 块、4 B 块号、12 个直接项时为 48KB+4MB+4GB+4TB=4402345721856 B 4.004 TB。前三项只占总量的万分之一,但问"最大文件大小"时答的就是这个和,只报三次间接的 4 TB 是漏项。

四部分逐项的可寻址块数与容量对照(合计 4 TB + 4 GB + 4 MB + 48 KB ≈ 4.004 TB;想逐级核对每一段各是多少时展开)
部分可寻址块数可寻址容量
直接地址项1212×4KB=48KB
一次间接10241024×4KB=4MB
二次间接1024210242×4KB=4GB
三次间接1024310243×4KB=4TB
合计4TB+4GB+4MB+48KB
换一组参数的完整走法:从 k 到四部分容量、再到某个偏移量要几次 I/O(想把这类计算的每一步都走一遍时展开)

设磁盘块大小 2 KB、每个块号占 4 B,索引结点中设 8 个直接地址项、一次/二次/三次间接各 1 个。求(1)单个文件最大大小;(2)索引结点已读入内存、无缓存命中时,访问文件偏移量 3 MB 处的一个字节需要几次磁盘 I/O。

第 1 步:算一个索引块能存几个块号。

k=2 KB4 B=20484=512

k 是后面每一级的公比,所有容量都是它的幂次乘块大小。先把 k 算出来,后面就不需要再想"这一级有几个块号"。

第 2 步:逐部分算可寻址容量。 每多一级间接就多乘一个 k,写成 kL×块大小 就不会数错级数。

部分可寻址块数容量
8 个直接地址项88×2KB=16 KB
一次间接512512×2KB=1 MB
二次间接5122=262144262144×2KB=512 MB
三次间接5123=134217728134217728×2KB=256 GB

第 3 步:求和。

16 KB+1 MB+512 MB+256 GB=275415842816 B256.5 GB

四部分寻址的是互不重叠的文件区间,所以最大文件大小是它们的而不是最大的那一项。这一步最容易漏——很多人只答 256 GB。

第 4 步:把累计阈值排出来,定位 3 MB 落在哪一级。

累计到覆盖的文件偏移范围
直接地址项016 KB
+ 一次间接16 KB16KB+1MB=1064960 B1.0156 MB
+ 二次间接1.0156 MB537935872 B513.02 MB

3 MB=3145728 B,大于 1064960、小于 537935872 → 落在二次间接范围内。判断落在哪一级不能凭感觉,必须把累计阈值算出来再比:一次间接覆盖的不是"01 MB"而是"16 KB 之后的 1 MB"。

第 5 步:数 I/O 次数。 每一级间接就是一个必须先读回来才能继续的索引块,最后再读一次数据块:

步骤读什么I/O
1读二次间接指向的一级索引块1
2读其中一项指向的二级索引块1
3读数据块(目标字节所在)1
合计3 次

换前提怎么调:索引结点不在内存 → 所有数字 +1;读一个完整数据块 vs 读一个字节 → I/O 次数相同(块是最小传输单位);跨块连续读取 N 字节 → 要算多个数据块的 I/O,但中间索引块只读一次;块号改为占 8 B → k 变成 2048/8=256,四部分容量与全部阈值都要重算,但 I/O 次数规律不变。

四、三种分配方式对比

分配方式判据:第 i 块的块号怎么来顺序存取随机存取外部碎片文件增长适用场景
连续分配起始块号 + i,纯加法最快支持困难文件大小固定、只读介质
隐式链接必须逐块读磁盘才知道下一块支持不支持方便顺序存取文件
显式链接(FAT)常驻内存的 FAT,不读盘支持支持方便FAT 文件系统
索引分配一次索引块拿到全部块号支持支持方便Unix/Linux

大文件索引组织:三者怎么选

方案定位第 i 块要读几个索引块空间开销什么时候选它
链接索引最坏 i/k 个(要沿链走)只为已用块建项,最省文件基本顺序访问;随机访问代价随文件增大而线性上升,是三者中唯一不支持高效随机存取
多级索引固定 L 个(L 为级数)小文件也要走满 L 级,小文件被惩罚文件大小分布集中(都很大),此时统一级数不吃亏
混合索引小文件 0 个,大文件最多 3 个小文件不建索引块,大文件按需展开文件大小分布极不均匀(大量小文件 + 少量大文件),即通用文件系统的真实情形

判据一句话:看文件大小的分布。 通用操作系统面对的一定是极不均匀那一种,所以 Unix 选了混合索引。

考点速记

  1. 三条路线的唯一分歧"第 i 个逻辑块在哪个物理块"这条映射存在哪里——存目录项(只需起点,靠加法算)→ 连续分配;存数据块自己里 → 隐式链接;抽出来集中存 → FAT 与索引分配。后面所有优缺点都是这一条选择的后果。
  2. 支不支持随机存取的统一判据:能不能不读磁盘就算出第 i 块的块号。 连续(起始块号 +i)、FAT(查常驻内存的表)、索引(读一次索引块拿到全部块号)都支持;⚠️只有隐式链接不支持
  3. 外部碎片只有连续分配有,判据是"分配是否要求物理上连成一片"。本质是空闲总量够,但没有一段够长;根治只有紧凑,而磁盘上做紧凑代价太大。⚠️内部碎片四种方式都有。 "必须预知文件大小""增长要整体搬家"与外部碎片同源。
  4. ⚠️隐式链接与显式链接只差"指针放哪":隐式放在每个数据块内部,要知道下一块必须先读回当前块 ⇒ 不支持随机存取;显式抽出集中成 FAT 并常驻内存,沿链跳转不访问磁盘 ⇒ 支持。这两条最容易记反。
  5. FAT 与索引块的规模差,判据是为谁建项:FAT 为全盘每一个块建项(哪怕空闲、哪怕属于别的文件),大小只随磁盘容量增长且须常驻内存;索引块只为本文件已占用的块建项。这是 FAT 撑不到大容量盘的根本原因。
  6. 索引分配的开销与文件大小无关,固定一块:4 KB 块下一个 1 KB 的文件实占 8 KB,利用率仅 12.5%混合索引正是为了消掉这一块——小文件用直接地址项,一个索引块都不建。
  7. 多级索引通式k=块大小块号字节数L 级可寻址 kL×块大小。⚠️1024 和 256 都只是 k 的取值,不是要背的数。
  8. ⚠️最大文件大小 = 四部分之和:直接项 + 一次 + 二次 + 三次间接寻址的是互不重叠的文件区间,所以要相加。只答"三次间接 4 TB"漏掉了另外三项。
  9. 访问 I/O 次数(索引结点已在内存):直接块 1 次 / 一次间接 2 次 / 二次间接 3 次 / 三次间接 4 次;索引结点还要读盘则各 +1。⚠️ 判断落在哪一级要把累计阈值算出来再比——一次间接覆盖的不是"0 起的 1 MB"而是"直接部分之后的 1 MB"。
  10. 直接地址项个数有教材分歧:UNIX System V 共 13 项、10 个直接;Linux ext2/ext3 共 15 项、12 个直接。做题以题目给出的个数为准。
  11. 块开大的两头代价:块大 ⇒ 块数少 ⇒ FAT/位示图小、索引层级浅、顺序读一次搬得多;但块大 ⇒ 每个文件平均浪费半个块的内部碎片

这一节在真题里被考过的形式

这是 file 章出题最密的一节——十余道题里有五道是大题分问。 但问法只有四类,按类练比按题练省得多。

  • ① 给场景问该选哪种分配方式(2009-28、2013-24、2020-24)。判据只有速记第二、三条。 2009-28 问"适合随机访问且易于文件扩展",答索引分配——连续分配随机访问也行但不易扩展 (要整体搬家),链接分配易扩展但隐式的不支持随机访问。2013-24 问"支持 CD-ROM 视频快速随机播放", 答连续分配——只读介质不存在扩展问题,外部碎片这个唯一缺点被场景消掉了, 而它的顺序读性能最好。⚠️ 这一类题的关键是看题干把哪个缺点消掉了。
  • ② 算最大文件大小(2010-30、2013-26、2018-46、2022-45)。固定四步: 先由块大小与块号字节数算 k → 分别算直接 / 一次 / 二次 / 三次间接各能寻址多少字节四部分相加 → 化成合适的单位。⚠️ 两处必错:漏加前三部分(速记第八条)、 把题目给的直接地址项个数换成记忆里的 10 或 12(速记第十条)。
  • ③ 给一个文件内偏移量,问要几次磁盘 I/O、或该块的块号在哪(2015-29、2018-46、2022-45、2026-46)。 做法是先算出四部分各自覆盖的累计区间,再看这个偏移落在第几段,然后套速记第九条的次数表。 ⚠️ 一次间接覆盖的区间起点是直接部分的终点,不是 0——直接拿"1 MB"去比会判错一级。
  • ④ 综合大题:给一套文件系统参数,连着问最大文件、目录项格式、块号位宽、字段优化 (2012-46、2014-46、2016-47、2026-46)。这一类把本章多节串起来, 常见分问有"目录项至少要多少字节"(= 文件名长度 + log2(块总数) 位换算成字节)、 "把某字段去掉能省多少"、"改成链接分配后插入一条记录要读写多少次盘"(2014-46)。 ⚠️ 2016-47 考的是 FAT 下按名存取的完整流程,要说清"查目录得首块号 → 顺 FAT 链跳转", 而FAT 常驻内存所以跳转不产生磁盘 I/O——这一句是给分点。

复习优先级必须拿满,且要练熟计算。 第②③类是每年大题的固定动作, 练到不看步骤也能走完;第①类靠速记第二、三条两句判据;第④类没有捷径, 但它的每个分问都能拆回前三类。最贵的两个坑是"四部分要相加"和"累计区间起点不是 0", 这两处每错一次就是整问失分。

易错:把隐式链接和显式链接的随机存取能力记反。指针在数据块里的(隐式)不支持,抽出来成 FAT 的支持

易错:算最大文件大小时只算三次间接。四部分寻址的是互不重叠的区间,必须相加

易错:凭记忆用"10 个直接地址项"或"12 个"。教材有两套口径,一律以题目给出的为准

易错:判断偏移落在哪一级时拿一次间接的容量直接比。一次间接的起点是直接部分的终点,要用累计阈值。

易错:认为索引分配对小文件也划算。开销固定一块,1 KB 文件在 4 KB 块下利用率只有 12.5%。

易错:认为 FAT 和索引块规模相当。FAT 为全盘每一块建项(含空闲块),索引块只为本文件已用块建项。

易错:认为只有连续分配有碎片。外部碎片只有它有,内部碎片四种都有

易错:认为连续分配一定不如索引分配。只读介质(CD-ROM)上它的唯一缺点被消掉,顺序读性能最好。

教材出处
  • 汤小丹《计算机操作系统》印刷版 p258–p259(8.1.4 增量式索引组织方式 / 图 8-8 混合索引方式):UNIX System V 的索引结点设 13 个地址项 i.addr(0)~i.addr(12),其中 10 个直接地址项,4 KB 块时"文件不大于 40 KB 时便可直接从索引结点中读出该文件的全部盘块号";一次间址块可存放 1 K 个盘块号、允许文件长达 4 MB;二次间址 4 GB;三次间址 4 TB。
  • 同书 p258 指出多级索引的主要缺点是"访问一个盘块时,其所需启动磁盘的次数随着索引级数的增加而增多,即使是对于小文件也是如此",并据此引出混合(增量式)索引——这正是本篇"为什么要留直接地址项"的依据。

相关知识

文件的逻辑结构目录文件元数据与索引节点外存空闲空间管理磁盘

真题练习