磁盘上只有编了号的块,用户要的是有名字的文件。文件系统要回答四件事:文件的描述信息放哪(FCB / inode)、按名字怎么找到它(目录)、它的数据放在哪些块上(物理分配)、哪些块还空着(空闲空间管理)。
文件 = 数据 + 描述它的信息。描述信息集中放在文件控制块 FCB 里,有了 FCB 才能按名存取。FCB 的有序集合就是目录,一个 FCB 就是一个目录项。
考试最常问:FCB 里有文件名、物理地址、存取权限;没有文件内容。
按名查找时只比较文件名,FCB 里其他信息在找到之前用不上,却也要跟着一起读盘。于是把它们拆出去放进 inode,目录项只留 文件名 + inode 号。目录项变小,一个盘块能放更多目录项,目录占的块就少,查找时读盘次数也就少。
UNIX 的目录项只有 16 B,其余信息(类型、权限、长度、地址项、链接计数 count …)都在 inode 里。
设盘块 1 KB,目录下有 640 个文件,顺序查找平均要读一半的目录块。
精确地说,顺序查找 k 个目录块平均读 (k + 1) ÷ 2 块;选择题一般按 k ÷ 2 估算。放在磁盘上的叫磁盘索引结点,文件打开后复制到内存的叫内存索引结点,后者多了 inode 编号、状态、访问计数、逻辑设备号等字段。
逻辑结构是用户看到的组织方式,和数据在磁盘上怎么放(物理结构)是两回事。无结构文件(流式文件)就是一串字节,源程序、可执行文件都是;有结构文件由一条条记录组成,重点是下面几种。
| 类型 | 怎么组织 | 按关键字找一条记录 | 优点 | 缺点 |
|---|---|---|---|---|
| 无结构(流式) | 以字节为单位的字节流,没有记录 | 只能穷举搜索 | 简单,适合源程序、可执行文件 | 不适合按记录查询 |
| 顺序文件 · 串结构 | 记录按存入时间排列,与关键字无关 | 从头顺序找,平均 N/2 次 | 批量顺序处理效率高 | 查找慢;增删记录难 |
| 顺序文件 · 顺序结构 | 记录按关键字排序 | 可折半查找,约 log2N 次 | 查找比串结构快 | 插入删除要移动记录(常配合日志文件,定期合并) |
| 索引文件 | 每条记录一个索引项(长度 + 指针),索引表按关键字排序 | 查索引表(可折半),再直接定位记录 | 变长记录也能随机访问 | 每条记录一个索引项,索引表可能很大 |
| 索引顺序文件 | 记录分组,每组第一条记录建一个索引项 | 先查索引表,再在组内顺序找,约 √N 次 | 索引表小得多,查找仍然快 | 组内仍要顺序查找 |
| 直接 / 散列文件 | 由关键字经散列函数直接算出地址 | 无需顺序查找,最快 | 存取速度快 | 可能冲突 |
N 条记录分成 √N 组,每组 √N 条。先在索引表(√N 项)里顺序找到组,平均 √N/2 次;再在组内顺序找,平均 √N/2 次。合计约 √N 次。
每条记录长 L,第 i 条(从 0 编号)的地址 = 起始地址 + i × L,直接算出来。变长记录就算不出来了,只能从头数,这正是索引文件要解决的问题。
注意区分:这里的“随机访问”指按记录号定位,和物理结构里“能否直接找到第 i 个盘块”是两个层面的问题。
下面是同一块磁盘(编号 0–15),文件 F 有 4 个逻辑块。看每种方式 FCB 里记了什么、要找逻辑块 2 该怎么走。
FAT 为磁盘上每个盘块设一个表项,表项里存下一块的块号(−1 表示文件结束,−2 表示空闲,所以 FAT 同时也管了空闲块)。
题目若要求表项按字节对齐,20 位要取 3 B,FAT = 3 MB。看清题目的对齐要求。
| 方式 | FCB 里记什么 | 随机访问 | 外部碎片 | 文件扩展 | 访问第 i 块读盘次数 | 主要代价 |
|---|---|---|---|---|---|---|
| 连续分配 | 起始块号、长度 | 支持 | 有 | 难 | 1 | 外部碎片,要预知文件大小 |
| 隐式链接 | 首块号、末块号 | 不支持 | 无 | 易 | i + 1 | 只能顺序访问;指针占空间;可靠性差 |
| 显式链接(FAT) | 首块号 | 支持 | 无 | 易 | 1 | FAT 常驻内存,占内存 |
| 索引分配(单层) | 索引块号 | 支持 | 无 | 易 | 2 | 索引块占空间,小文件浪费 |
约定:逻辑块号 i 从 0 开始;FCB 已在内存(文件已打开);索引分配的索引块不在内存,所以要先读 1 次索引块。若题目说索引块已调入内存,就只要 1 次。
改盘块大小、地址项长度和各级地址项个数,下面实时算出各级能管多少块、单个文件最大多长;再输入一个文件内的字节偏移,看它落在哪一级、要读几次盘、每层索引块里用第几个下标。
| 级别 | 地址项 | 可寻址块数 | 容量 | 逻辑块号范围 | 读盘次数 |
|---|
约定:文件已打开,inode 已在内存,各级索引块都不在内存。读盘次数 = 要读的索引块数 + 1 次数据块。若题目说 inode 不在内存(文件还没打开,要先把 inode 读进来),再 +1。
管理的对象是整个文件卷(分区)上的空闲块。前两种和内存动态分区很像,位示图和成组链接是计算题高发区。
| 方法 | 怎么记 | 分配 | 回收 | 特点 |
|---|---|---|---|---|
| 空闲表法 | 每个连续空闲区一项:第一个空闲块号、空闲块数 | 首次适应、最佳适应等,和内存动态分区一样 | 与相邻空闲区合并(四种情况) | 适合连续分配 |
| 空闲链表 · 盘块链 | 所有空闲块串成一条链,以块为单位 | 从链头摘 k 块 | 挂到链尾 | 简单;但一次分配或回收多块时要重复多次,效率低 |
| 空闲链表 · 盘区链 | 每个连续空闲区(盘区)为一个结点,结点里记区大小 | 首次适应等 | 要与相邻盘区合并 | 分配回收效率高,但更复杂 |
| 位示图 | 每块一位:0 空闲、1 已分配(以题目为准) | 扫描找 0 位 → 算出块号 → 置 1 | 由块号算出行列 → 置 0 | 位示图小,可放内存;换算公式是考点 |
| 成组链接法 | UNIX:空闲块分组,每组的信息记在上一组的某一块里,第一组的信息在超级块 | 见下 | 见下 | 空闲块表不占额外连续空间,适合大文件系统 |
点网格里任意一格,可直接把它设为当前盘块。灰底格是 1(已分配),白底是 0(空闲)。混合编号(例如盘块号从 0、行列从 1)时,先把行列各减 1 化成全从 0,再套左边的公式:b = n(i − 1) + (j − 1)。
超级块在文件系统挂载时读入内存,平时分配回收都在内存里改,只有换组时才读写一次盘。
按路径查目录要一层层读盘,很慢。open 把这件事只做一次:查到 FCB(inode)后放进内存的打开文件表,返回一个编号(文件描述符 fd);之后 read / write 都用 fd,不再按路径查目录。
| 操作 | 系统做了什么 |
|---|---|
| create | ① 为文件分配外存空间;② 在目录中新建目录项(文件名、位置等) |
| delete | ① 按路径找到目录项;② 回收文件占用的磁盘块;③ 删除目录项。有硬链接时只是 count − 1 |
| open | ① 按路径查目录找到目录项;② 检查权限;③ 把 FCB(inode)复制进内存打开文件表;④ 返回 fd。不读文件数据 |
| close | ① 删除进程打开文件表中的表项;② 系统打开文件表的打开计数 − 1,减到 0 删除该表项(FCB 有修改就写回外存) |
| read | 给出 fd、读入内存的位置、读多少;从读写指针处开始读,读完指针后移 |
| write | 给出 fd、内存中数据的位置、写多少;从读写指针处写 |
进程级表:每个进程一张,记这个进程自己的读写指针、访问权限,以及指向系统表项的指针。两个进程读同一个文件,各读各的位置,所以指针放在这里。
系统级表:整个系统一张,每个已打开文件一项,放 FCB / inode 副本、打开计数、磁盘位置等。第二个进程再 open 同一文件时,只把计数 + 1,不再重复读 FCB。
磁盘上的布局:整个磁盘有主引导记录 MBR 和分区表;每个分区依次放 引导块、超级块、空闲空间管理信息、inode 区、根目录,然后是其余文件和目录。
每种文件系统都要实现 VFS 规定的函数。VFS 定义了四类对象:超级块对象(一个已挂载的文件系统)、索引结点对象(一个文件)、目录项对象(路径中的一项,只存在于内存)、文件对象(一个被进程打开的文件)。
| 分配方式 | 随机访问 | 外部碎片 | 访问第 i 块读盘 | 一句话原因 |
|---|---|---|---|---|
| 连续分配 | 支持 | 有 | 1 | 起始块号 + i 直接算出物理块号 |
| 隐式链接 | 不支持 | 无 | i + 1 | 下一块的地址藏在上一块里,只能一块块读过去 |
| 显式链接 FAT | 支持 | 无 | 1 | 指针链在内存里的 FAT 中走完,只读数据块 |
| 单层索引 | 支持 | 无 | 2 | 读索引块查第 i 项,再读数据块 |
| k 层索引 | 支持 | 无 | k + 1 | 每层索引块各读一次 |
| 混合索引 | 支持 | 无 | 1 / 2 / 3 / 4 | 直接 / 一级 / 二级 / 三级间接(inode 已在内存) |