1. 文件结构
文件结构分为两个维度:逻辑结构 是用户视角,描述文件内部数据的组织形式;物理结构是操作系统视角,描述文件数据在磁盘上的实际存放方式。两者相互独立------用户只需关心逻辑地址,操作系统负责将其转换为物理地址,各司其职。
2. 逻辑结构
文件逻辑结构探讨的是:在用户眼中,文件内部的数据应该以何种形式组织?这与数据结构中的逻辑结构和物理结构概念是类似的------线性表是一种逻辑结构,可以用顺序表(元素物理相邻,支持随机访问)或链表(元素物理离散,不支持随机访问)两种物理结构实现。文件也是如此,逻辑结构与物理结构共同影响文件操作的具体实现。
文件按内部结构分为两大类:
- 无结构文件(流式文件) 由字节或字符流构成,无明显结构,如
.txt,无需讨论逻辑组织; - 有结构文件(记录式文件) 由一条条记录 组成,每条记录由若干数据项 构成(数据项是文件系统最基本的数据单位),如数据库表。记录中可指定某一数据项作为关键字,用于唯一标识每条记录。
根据各条记录的存储空间是否相等,又分为定长记录 和可变长记录。
定长记录:每条记录长度固定,每个数据项在记录中的位置也相同。
可变长记录:各记录长度不等,是实际应用中最常见的形式。
有结构文件的逻辑结构分为以下三种:
2.1 顺序文件
文件中的记录一个接一个顺序排列,可以是定长或可变长记录。按记录是否按关键字排列,分为两种:串结构 (记录顺序与关键字无关,通常按存入时间排列)和顺序结构 (记录严格按关键字顺序排列,支持折半查找等高效检索)。在物理实现上,可采用顺序存储 (逻辑相邻的记录物理上也相邻)或链式存储(记录间用指针链接,物理位置可不相邻)。
是否支持随机存取,取决于以下组合:
| 存储方式 | 记录类型 | 随机存取 | 原因 |
|---|---|---|---|
| 链式存储 | 定长/可变长 | ✗ | 各记录物理位置无规律,只能从头顺序查找 |
| 顺序存储 | 可变长记录 | ✗ | 见下方分析 |
| 顺序存储 | 定长记录 | ✓ | 第 i 条记录逻辑地址 = i × L(L 为记录长度),可直接定位 |
可变长记录为何不支持随机存取:每条可变长记录前需要一个额外字节记录该记录的长度。设各记录长度分别为 l₀、l₁、l₂...,则第 i 条记录的逻辑地址 = (l₀+1) + (l₁+1) + ... + (lᵢ₋₁+1),由于各 lₖ 无规律,必须从第 0 条开始依次累加才能确定,无法直接计算。
增删操作:顺序存储的顺序文件增删记录代价较大;串结构相对灵活,新记录直接追加到文件末尾即可。实践中,操作系统通常维护一个日志文件记录修改操作,定期批量合并到磁盘文件,以减少磁盘 I/O 次数。
2.2 索引文件
引入动机:可变长记录的顺序文件查找第 i 条记录时,只能从头逐条扫描。为让可变长记录也能实现快速随机访问,引入了索引文件。
每个文件对应一张索引表 ,每个索引项记录「索引号(关键字)、记录长度、记录起始逻辑地址」,每个索引项长度固定 (如三字段各占 4 字节,共 12 字节)。因此索引表本身就是一张定长记录的顺序文件,可支持随机访问。若索引表按关键字有序排列,还可以用折半查找实现高效检索。
文件的实际记录在物理上无需连续,但索引表的各表项需连续存放。查找时:先在索引表中定位目标记录的逻辑地址,再按逻辑地址读取记录。索引表由用户(文件创建者)维护,增删记录时须同步更新索引表。
还可以为同一文件的不同字段分别建立索引表(如既按学号建立索引,又按姓名建立索引),实现多维度快速检索------数据库中 CREATE INDEX 语句即是此思想的具体实现。索引文件因检索速度快,常用于对信息处理及时性要求高的场合。
2.3 索引顺序文件
引入动机:索引文件中每条记录对应一个索引项,若记录平均只占 8 字节,但每个索引项占 32 字节,则索引表的体积是文件内容的 4 倍,存储利用率低。
思想 :将记录分组,每组共享一个索引项(指向该组第一条记录的逻辑地址),组内记录顺序扫描。这样索引表项大幅减少,是索引文件与顺序文件思想的结合。索引顺序文件的索引表本身是一个定长记录的串结构顺序文件(不要求按关键字排序,便于插入新索引项)。
若文件规模更大(如 10⁶ 条记录),可建立多级索引顺序文件 :先建低级索引表(每张表对应 100 组),再为低级索引表建顶级索引表,形成两级结构。此时平均查找次数 = 50 + 50 + 50 = 150 次,检索效率进一步提升。
3. 物理结构(文件分配方式)
文件物理结构探讨的是:操作系统如何将文件的逻辑块存放到磁盘上,以及如何完成逻辑块号 → 物理块号的映射。
磁盘块与逻辑地址 :磁盘被划分为大小相等的磁盘块(物理块) 并依次编号。0号块、1号块......磁盘块大小通常与内存页面大小相同(内存与外存按块交换数据,大小一致方便变换)。类比内存分页,文件的逻辑地址空间也按块划分,用 (逻辑块号, 块内地址) 表示。例如物理块大小为 1 KB 时,1 MB 的文件被分割为 1024 个逻辑块(块号 0‖1023)。
用户操作文件时只提供逻辑块号和块内地址,各逻辑块实际存放在哪个物理块对用户透明,操作系统负责将逻辑块号转换为物理块号。文件分配方式共三种:连续分配 、链接分配 (隐式 / 显式)、索引分配(单级 / 多级 / 混合)。
3.1 连续分配
每个文件在磁盘上占用一组连续 的磁盘块,逻辑相邻的块物理上也必须相邻。FCB 记录文件的起始块号 和长度(块数),物理块号 = 起始块号 + 逻辑块号,操作系统还需验证逻辑块号是否超过文件长度。
地址映射示例 :文件 aaa 起始块号 = 4、长度 = 3,访问逻辑块 2 时,物理块号 = 4 + 2 = 6。文件 bbb 起始块号 = 10、长度 = 4,占用物理块 10、11、12、13。
优点 :支持顺序访问和随机访问,且顺序读写速度最快------连续块物理上相邻,寻道时磁头移动距离最短(访问非连续块时,磁头每次跳距较大,读写耐时较长)。
缺点:文件拓展不方便(后续块可能已被占用,需整体迁移);离散空闲块难以利用,产生外部磁盘碎片(类似内存外部碎片,可用紧凑解决但代价大),存储利用率低。
3.2 链接分配
为文件分配离散的磁盘块,用指针将各块串联起来,分为隐式和显式两种。
隐式链接 :每个磁盘块尾部保留少量空间存放指向下一块的指针(对用户透明),FCB 记录起始块号和结束块号。
访问第 i 号逻辑块的过程:操作系统找到 FCB 中的起始块号,先读入 0 号块;从 0 号块尾部的指针取得 1 号块的物理块号,再读入 1 号块;以此类推,直到读入第 i-1 号块,才能得到 i 号块的物理块号。因此访问第 i 号逻辑块总共需要 i+1 次磁盘 I/O ,只支持顺序访问,不支持随机访问。拓展文件方便,无外部碎片。
显式链接(FAT) :将所有块的链接指针显式统一存放在一张文件分配表(FAT) 中,一个磁盘只对应一张 FAT,FAT 在开机时读入内存并常驻内存 。FCB 只需记录起始块号,查 FAT 不需读磁盘,因此支持随机访问,访问效率远高于隐式链接。缺点是 FAT 本身占用一定内存空间。
FAT 表格示例:文件 aaa 依次占用物理块 2、5、0、1,文件 bbb 依次占用 4、23、3:
| 物理块号 | 下一块 |
|---|---|
| 0 | 1 |
| 1 | -1(aaa 末尾) |
| 2 | 5 |
| 3 | -1(bbb 末尾) |
| 4 | 23 |
| 5 | 0 |
| 23 | 3 |
| ... | ... |
aaa 的 FCB 记起始块 = 2,查表可得链: 2 → 5 → 0 → 1。访问 aaa 的 2 号逻辑块:从起始块 2 出发,查表得 5(即 1 号逻辑块),再查表得 0(即 2 号逻辑块对应物理块 0)。整个过程无需读磁盘。
3.3 索引分配
索引分配与链接分配类似,同样允许为文件分配离散的磁盘块。系统为每个文件建立一张索引表 ,记录各逻辑块对应的物理块号(类比内存管理中的页表)。存放索引表的磁盘块称为索引块 ,存放文件实际数据的块称为数据块,FCB 中记录该文件索引块的块号。
具体示例:文件 aaa 的数据依次存放在物理块 2、5、10、3、9,索引表存放在 7 号块(即索引块):
| 逻辑块号 | 物理块号 |
|---|---|
| 0 | 2 |
| 1 | 5 |
| 2 | 10 |
| 3 | 3 |
| 4 | 9 |
每个索引项长度固定(4 字节),逻辑块号可由表项序号隐含推导。与 FAT 的区别:索引分配中每个文件拥有自己的索引表,而 FAT 是一个磁盘只对应一张。
地址映射过程 :访问逻辑块 i -> FCB 找索引块号 -> 读入索引块 -> 查索引表第 i 项得物理块号 -> 读入数据块,共需 2 次磁盘 I/O (索引块已在内存则只需 1 次)。不需顺序访问前 i-1 块,支持随机访问。拓展文件只需找一个空闲块并在索引表中增加表项。缺点是索引表本身占用一定存储空间。
索引表过大的问题:设块大小 1 KB、每项 4 字节,则一个块最多存 256 个索引项。若文件超过 256 块,索引表就装不下。有三种解决方案:
链接方案:为索引表分配多个索引块,用链接指针依次串联。缺点:如文件大小为 64 MB,需要 256x256 个磁盘块,对应 256x256 个索引项,共需 256 个索引块依次链接。访问最后一个逻辑块时,必须先依次读入前 255 个索引块,地址转换可能需要 200 多次读磁盘,效率极低。
多层索引 :类比多级页表,建立多级索引结构,第一级索引块指向第二级,以此类推。各级索引表均不超过一个磁盘块。k 层索引(顶级索引表未在内存)访问一个数据块需 k+1 次读磁盘。
以每块 256 项、块大小 1 KB 为例:两层索引最大文件 = 256 x 256 x 1 KB = 64 MB ,三层最大 = 256 x 256 x 256 x 1 KB = 16 GB。
地址转换示例 (两层索引,访问逻辑块 1026):1026 / 256 = 4(商),1026 % 256 = 2(余)。即查一级索引表第 4 项得二级索引表块号,再查该二级索引表第 2 项得到物理块号。共 3 次读磁盘。缺点:即使小文件也需 k+1 次读磁盘。
混合索引 :顶级索引表同时包含直接地址索引(直接指向数据块)、一级间接索引和二级间接索引。以 8 个直接地址 + 1 个一级间接 + 1 个二级间接(每块 256 项、1 KB)为例:
- 文件最大长度 = (8 + 256 + 256 x 256) x 1 KB = 65800 KB
- 访问逻辑块 0~7(直接地址):读顶级索引表 + 读数据块 = 2 次 I/O
- 访问逻辑块 8~263(一级间接):读顶级索引表 + 读一级索引表 + 读数据块 = 3 次 I/O
- 访问逻辑块 264 以上(二级间接):读顶级 + 读一级 + 读二级索引表 + 读数据块 = 4 次 I/O
混合索引对小文件最友好,读磁盘次数少,而计算机中小文件展常属多数。
7. 物理结构对比
| 分配方式 | FCB 记录内容 | 随机访问 | 顺序读写速度 | 文件拓展 | 存储利用率 |
|---|---|---|---|---|---|
| 连续分配 | 起始块号 + 长度 | ✓ | 最快 | 不方便 | 低(有碎片) |
| 隐式链接 | 起始块号 + 结束块号 | ✗ | 慢 | 方便 | 高 |
| 显式链接(FAT) | 起始块号 | ✓ | 较快 | 方便 | 高 |
| 索引分配 | 索引块块号 | ✓ | 较快 | 方便 | 高 |
8. 逻辑结构与物理结构的关系
逻辑结构和物理结构是完全独立的两个维度,容易混淆的概念对比如下:
| 概念 | 层次 | 由谁决定 | 映射关系 |
|---|---|---|---|
| 顺序文件的链式存储 | 逻辑结构 | 用户 | 记录关键字 → 下一条记录逻辑地址 |
| 链接分配 | 物理结构 | 操作系统 | 逻辑块 → 下一物理块块号 |
| 索引文件的索引表 | 逻辑结构 | 用户 | 关键字 → 记录的逻辑地址 |
| 索引分配的索引表 | 物理结构 | 操作系统 | 逻辑块号 → 物理块号 |
无论文件采用何种逻辑结构,操作系统都可以选择任意物理结构来存储;无论采用何种物理结构,用户使用文件时始终只需提供逻辑地址。