文件管理(四)—— 文件存储空间管理

前言

文件物理结构解决的是非空闲磁盘块 的组织问题,而本节讨论的存储空间管理,则是对空闲磁盘块的管理。核心问题围绕三个角度展开:① 用什么数据结构记录空闲块?② 如何分配磁盘块?③ 如何回收磁盘块?

1. 存储空间的划分与初始化

文件卷(逻辑卷/逻辑盘):即 Windows 中的 C 盘、D 盘等,是操作系统管理文件的基本单位。一个物理磁盘可以划分成多个文件卷;某些系统也支持多个物理磁盘合并为一个文件卷(用于超大型文件系统)。

每个文件卷内部进一步划分为两个区域:

区域 存放内容
目录区 文件目录(FCB/inode)、空闲表、位视图、超级块等管理数据
文件区 普通文件的实际数据

2. 空闲表法

用一张空闲表记录每个连续空闲区间的起始盘块号和长度,与内存管理中的动态分区分配空闲表原理相同。

分配 :采用首次适应、最佳适应或最坏适应等算法,从空闲表中找到满足需求的连续空闲区,摘出所需块并更新表项。适用于连续分配的物理结构。

回收:回收时需根据回收区与已有空闲区的相邻关系分四种情况处理(与动态分区分配回收相同):

情况 处理方式 表项数量变化
前后都无相邻空闲区 新增一个表项 +1
前后都有相邻空闲区 合并为一个表项 -1
仅前面有相邻空闲区 扩展前面表项 不变
仅后面有相邻空闲区 扩展后面表项 不变

空闲表法不适合大型文件系统,因为空闲表可能变得很长。

3. 空闲链表法

以链表形式组织空闲块,分为两种变体,盘块(block)是磁盘分配的基本单位,盘区(extent)是一组连续盘块组成的区域:

3.1 空闲盘块链

单个盘块为节点组成链表,每个空闲盘块中存有指向下一个空闲盘块的指针。操作系统保存链头和链尾指针。

  • 分配 :从链头依次摘下 k 个盘块,修改链头指针。适用于离散分配
  • 回收:将回收的盘块挂到链尾,修改链尾指针。

缺点:分配多个盘块需多次操作,效率偏低。

3.2 空闲盘区链

连续空闲区间(盘区) 为节点,每个盘区的第一个盘块中记录该区长度和下一个盘区的指针。

  • 分配 :用首次适应等算法在链中找合适的盘区;若无连续的 k 个块,可跨盘区拼凑分配。既适用于连续分配 ,也适用于离散分配
  • 回收:与空闲表法类似,注意相邻盘区的合并。

空闲盘区链一次可摘出一整片连续空间,分配多个块时效率高于空闲盘块链。

4. 位视图法(位图法)

二进制位串 表示磁盘空间状态,每一位对应一个盘块:0 表示空闲,1 表示已分配(具体含义以题目为准)。

4.1 数据组织

位视图通常按字存储,若字长为 n 位,则用 (字号 i, 位号 j) 二元组定位某一盘块。

转换公式 (以字号、位号、盘块号均从 0 开始为例):

盘块号=n×i+j\text{盘块号} = n \times i + j 盘块号=n×i+j

i= ⌊盘块号n⌋ ,j=盘块号  mod  n i = \left\lfloor \frac{\text{盘块号}}{n} \right\rfloor, \quad j = \text{盘块号} \bmod n i=⌊n盘块号⌋,j=盘块号modn

若从 1 开始编号,公式需相应调整。题目中一定要看清楚是从 0 还是从 1 开始。

示例(字长 n = 16):

  • 字号 1、位号 10 → 盘块号 = 16×1 + 10 = 26
  • 盘块号 31 → 字号 = 31÷16 = 1 ,位号 = 31%16 = 15

4.2 分配与回收

  • 分配:顺序扫描位视图,找到 k 个为 0 的位,由字号和位号计算出盘块号,将对应位置 1。连续/离散分配均适用。
  • 回收:由回收盘块号计算字号和位号,将对应位置 0。

5. 成组链接法

成组链接法是 Unix 文件系统中常用的一种空闲空间管理方法,适用于大型文件系统。它通过将大量空闲盘块分组管理,避免了空闲表过大或空闲链表过长的问题,提高了空闲空间管理效率。

5.1 数据结构

成组链接法的基本思想是:

将空闲盘块分成若干组,每组保存一批空闲盘块的编号,同时组与组之间形成链接关系。

系统使用超级块(superblock) 保存当前第一组空闲盘块的信息。超级块属于文件系统元数据区域,系统启动时会将其读入内存并长期保存,因此访问第一组信息时不需要读磁盘。

例如:

css 复制代码
超级块:
[空闲块数=100 | 空闲盘块号:201,202,...,300]
                         ↓

300号盘块:
[空闲块数=100 | 空闲盘块号:301,302,...,400]
                         ↓

400号盘块:
[空闲块数=100 | 空闲盘块号:401,402,...,500]
                         ↓

最后一组:
[空闲块数 | 空闲盘块号列表 | 下一组=-1]

其中:

  • 超级块保存第一组空闲盘块的信息;
  • 每组的最后一个空闲盘块用于保存下一组空闲盘块的信息;
  • 最后一组没有下一组,因此使用特殊值(如 -1)表示结束。

需要注意:

  1. 每组中的盘块号不要求连续;
  2. 每组最多保存固定数量的空闲盘块信息;
  3. 由于每组需要占用一个盘块保存下一组信息,因此该盘块不能直接作为普通空闲盘块使用。

5.2 分配过程

分配一个盘块

假设超级块中记录:

复制代码
空闲块数 = 100   空闲盘块:201,202,...,300

当文件需要一个盘块时:

  1. 操作系统直接从超级块中取出一个空闲盘块号,例如 201;
  2. 将盘块 201 分配给文件;
  3. 修改超级块中的空闲块数量:100 → 99

由于超级块已经在内存中,因此整个过程不需要访问磁盘。

分配一整组盘块

如果第一组空闲盘块全部被分配,只剩下最后一个盘块(例如300号盘块)时,需要特殊处理。

原因:

300号盘块中保存了下一组空闲盘块的信息,如果直接分配,会导致后续空闲盘块丢失。

因此步骤如下:

  1. 先读取300号盘块中的下一组信息;
  2. 将下一组信息复制到超级块中;
  3. 再将300号盘块分配给文件。

处理后:

复制代码
原:超级块 → 300号盘块 → 下一组

变为:超级块 → 下一组

保证超级块始终保存当前可用的第一组信息。

5.3 回收过程

回收盘块时,需要把释放的盘块重新加入第一组。

情况一:当前组未满

超级块:空闲块数 = 80

此时还有空间记录新的空闲盘块。

回收一个盘块:

  1. 将该盘块号加入当前组;
  2. 修改空闲块数:80 → 81

即可完成回收。

情况二:当前组已满

超级块:空闲块数 = 100

当前组已经无法继续加入新的盘块。

此时:

  1. 将超级块中当前组的信息复制到被回收的盘块中;

    复制代码
    回收盘块:
    
    保存旧第一组的信息
  2. 修改超级块,使其指向这个新的盘块;

  3. 将超级块中的空闲块数设置为 1。

结果:新的空闲盘块组被插入链表最前端。

6. 小结

方法 数据结构 适用分配方式 适用规模
空闲表法 空闲区间表 连续分配 小型
空闲盘块链 单块链表 离散分配 小型
空闲盘区链 盘区链表 连续/离散 中型
位视图法 二进制位串 连续/离散 通用
成组链接法 分组链 + 超级块 离散分配 大型(Unix)
相关推荐
黄敬峰17 小时前
一文搞懂 LLM 流式输出与 SSE
面试
Rain的Java大神之路18 小时前
JavaWeb开发如何解决跨域问题
java·前端·后端·nginx·web安全·面试·运维开发
职场的momo18 小时前
字节生活服务海量内推,挑战亿级订单与AI交易中台
人工智能·程序人生·面试·职场和发展·跳槽·生活·业界资讯
hezhifu20 小时前
告别臃肿与局限,FlyEnv 让本地开发环境管理更便捷
操作系统
黄敬峰1 天前
一文搞懂 AI 聊天应用的 Memory 模块:Milvus 向量数据库实战
面试
CoderYanger1 天前
前端基础——JavaScript(基础语法)(下篇)
java·开发语言·前端·javascript·程序人生·面试·职场和发展
噜~噜~噜~1 天前
操作系统笔记-2.4.1 死锁的概念
笔记·操作系统
~木雨1 天前
Java 线程池七问七答:参数、执行流程、拒绝策略到 ThreadLocal 内存泄漏,面试必背
java·面试·线程池·threadlocal·threadpool·executor
ocean21031 天前
2025-2026年计算机网络面试高频知识点洞察
计算机网络·面试·职场和发展·https·tcp·面试真题·秋招春招
lee_curry2 天前
AI Agent 工程师完整学习路线(面向生产级项目与面试)
人工智能·学习·ai·面试·agent