主题
第六章 文件系统:持久化的抽象
1. 为什么应用不直接读写磁盘块
进入三大主线的最后一条:持久化。内存断电即失,重要数据必须落到磁盘。但磁盘的原始接口极其简陋:它只认识"读第 x 块、写第 y 块",每块固定 512 字节或 4KB。如果让应用直接面对这个接口,每个程序都要自己回答一堆难题:
- 我的数据放在哪些块上?块号自己记?
- 两个程序都想用第 100 块怎么办?
- 别的用户能不能偷看我的块?
- 断电时写到一半怎么办?
操作系统的回答是再造一层抽象——文件系统。它把"编号的块的海洋"包装成两个友好概念:
- 文件:一段有名字的、长度可变的字节序列。应用只管按名字打开、按偏移读写,数据具体落在哪些块由文件系统记账。
- 目录:把文件组织成树,提供"路径名"这种人类友好的定位方式。目录本质上也是一种文件,内容是"名字 → 文件元数据位置"的对照表。
一句话点透:文件系统之于磁盘,就像页表之于内存——都是把"用户看到的连续逻辑空间"映射到"零散的物理空间"的翻译账本。
2. 文件的逻辑结构与物理结构
先分清两个"结构":逻辑结构是用户看到的文件内部组织(流式的无结构文件、有格式的记录式文件);物理结构是文件的字节实际怎样摆到磁盘块上。考试重头戏是物理结构的三种分配方式。
2.1 三种分配方式
连续分配:文件占据磁盘上一段连续的块,账本只需记"起始块 + 长度"。
链接分配:文件的块散落各处,每块尾部存一个指针指向下一块(隐式链接);或把所有指针集中到一张表里(显式链接,FAT 文件分配表就是这个思路)。
索引分配:为每个文件建一个索引块,把它所有数据块的块号列成清单。想读第 i 块,查索引清单第 i 项即可。
| 对比项 | 连续分配 | 链接分配 | 索引分配 |
|---|---|---|---|
| 顺序访问 | 极快 | 可以,逐块跟指针 | 快 |
| 随机访问 | 极快,起始块加偏移直接算 | 很慢,必须从头顺藤摸瓜 | 快,查索引表直达 |
| 文件增长 | 困难,后面没空位就得整体搬家 | 容易,链上再挂一块 | 容易,索引表加一项 |
| 碎片 | 产生外碎片 | 无外碎片 | 无外碎片 |
| 额外开销 | 几乎没有 | 每块一个指针;显式链接需整张 FAT 常驻内存 | 每个文件一个索引块,小文件也要付这笔账 |
| 可靠性 | 好 | 一个指针坏,后半截全丢 | 索引块坏则全文件丢,通常有备份 |
记忆抓手:连续分配像一排连号座位,链接分配像寻宝游戏每站给下一站地址,索引分配像目录页直接列出所有座位号。
2.2 UNIX 多级索引:inode 的直觉
索引分配有个尴尬:索引块大小固定,大文件的块号清单装不下。UNIX 的 inode(索引节点,每个文件一个,存放文件的所有元数据和索引信息)用分级解决:
设计动机很漂亮:绝大多数文件很小,少数文件很大。小文件只用直接块,零额外开销;文件越大,才逐级动用间接块,支持的容量按"每级乘以每块可存的块号数"指数扩张。这和多级页表是同一个思想——按需展开的树。
顺带一个高频计算题型:块大小 1KB、块号 4 字节,则一个间接块可存 256 个块号;一级间接支持 256KB,二级间接支持 256 × 256KB = 64MB,以此类推。
