操作系统

文件管理负责组织和管理存储设备上的数据,为用户提供按名存取的文件操作接口。核心任务包括文件逻辑结构与物理结构的设计、目录组织与管理、磁盘空间分配与回收、文件共享与保护机制,确保数据存储的可靠性、安全性与高效性。

文件与目录

文件与目录

文件

  • 文件是操作系统中最为常见的对象
  • 有名称的一组相关信息的集合
  • 文件是信息的一种抽象机制,提供将信息保存在外存并方便读取的手段

文件结构

逻辑结构(用户视角)

  • 流式文件(字节流):文件由字节序列组成,无内部结构,如 .txt, .bin
    • UNIX/Linux 默认采用流式文件,所有文件均视为无结构的字节流
  • 记录式文件:文件由若干逻辑记录组成,每条记录有特定结构
    • 如数据库文件,每条记录包含多个字段(学号、姓名、成绩等)
    • 早期 OS(如 IBM OS/360)广泛使用,现代 OS 已较少直接在文件系统层支持

物理结构(存储设备上的组织方式)

  • 连续结构:文件占用磁盘上连续的物理块
    • 优点:顺序访问性能好(磁头移动少),直接访问易实现(通过偏移量计算块号)
    • 缺点:需预知文件大小,易产生外部碎片,不利于文件增长
  • 链接结构:文件的数据块通过指针链链接在一起
    • 隐式链接:每个数据块含指向下一块的指针,只支持顺序访问
    • 显式链接(FAT):将所有链接指针集中存放在文件分配表(FAT)中,支持直接访问
    • 优点:无外部碎片,易于动态增长
    • 缺点:顺序访问慢(随机IO),指针占用存储空间
  • 索引结构:为每个文件建立索引块,存放所有数据块的指针
    • 优点:直接访问快,无外部碎片
    • 缺点:索引块本身占用额外空间
    • UNIX 采用混合索引(多级索引),兼顾小文件和大文件

访问方式

  • 顺序访问:按字节/记录从前向后顺序读写
    • 文件指针自动后移,如磁带式访问
    • 适用于大多数文本文件、日志文件
  • 直接访问(随机访问):指定位置(字节偏移或记录号)直接读写
    • 如数据库、索引文件
    • 支持 lseek() 定位后读写

文件控制块(FCB)

  • 为管理文件而设置的数据结构,保存文件的全部属性信息
  • 包含以下主要字段:
    • 文件名:用户可见的文件标识
    • 文件类型:如普通文件、目录文件、特殊文件等
    • 文件号(inode 号):文件系统内部的唯一标识
    • 文件长度:当前文件大小(字节数)
    • 存储位置:文件数据在磁盘上的物理位置(块号指针)
    • 访问权限:所有者、同组用户、其他用户的读/写/执行权限
    • 时间戳:创建时间、最后修改时间、最后访问时间
    • 链接计数:有多少个文件名指向该文件(硬链接计数)
    • 在 UNIX 中,FCB 即为 inode(索引节点),但 inode 不包含文件名,文件名存放在目录文件中

文件目录结构

  • 目录:以文件形式存在(目录文件),内容是一系列 FCB 或目录项的集合
  • 目录的作用:实现”按名存取”,将文件名映射为文件的物理位置

单级目录

  • 所有文件放在一个目录中,系统只有一张线性(或哈希)文件目录表
  • 优点:简单,易于实现
  • 缺点:名称冲突(不同用户不能使用相同文件名),不支持文件分组,线性搜索效率低

二级目录

  • 分为主文件目录(MFD)用户文件目录(UFD)
  • MFD 记录各个用户的用户名和其 UFD 的位置
  • 每个用户拥有自己的 UFD,只包含该用户的文件
  • 优点:不同用户可使用相同文件名,支持多用户隔离
  • 缺点:用户内部仍无法对文件分类组织,“路径”概念开始出现(如 /userA/file1

树形目录

  • 多级层次结构,支持文件分组
  • 引入当前目录(工作目录):用户当前所在的目录,简化路径表示
  • 绝对路径:从根目录(/)开始的完整路径
    • /home/user/docs/report.txt
  • 相对路径:从当前目录开始的路径
    • ./docs/report.txtdocs/report.txt
  • 优点:层次清晰,支持文件分组,不同目录下可包含同名文件
  • 缺点:共享文件需通过路径链接

无环图目录

  • 在树形目录基础上增加共享链接,允许多个路径指向同一文件
  • 支持文件共享(通过硬链接或符号链接实现)
  • 需将 FCB 与文件内容分离:FCB 中存放文件物理位置,目录项中存放文件名和 FCB 指针
  • 多个目录项可指向同一个 FCB,实现共享
  • 注意:即使从某个目录中删除该文件,只要还有其他链接,文件内容就不应删除(引用计数机制)

文件存储空间管理

  • 操作系统需要跟踪磁盘上空闲块,分配新块给文件,回收删除文件释放的块

空闲表法

  • 系统维护一张空闲表,记录每个连续空闲区的起始块号和块数
  • 适用于连续结构文件分配

空闲链表法

  • 将所有空闲块链接成一个链表,分配时从链头取块,回收时挂回链尾
  • 空闲盘块链:以数据块为单位链接
  • 空闲盘区链:以连续空闲区为单位链接

位图法(Bit Map)

  • 用一个二进制位(bit)对应一个物理块,0 表示空闲,1 表示已占用
  • 优点:查找空闲块效率高(利用 CPU 位操作指令),占用空间小
  • 缺点:位图需全部调入内存才能工作(大容量磁盘下位图较大)
  • 现代文件系统(如 ext 系列)广泛采用位图管理

成组链接法(UNIX 系统)

  • 将空闲块分组管理,每 50 个空闲块为一组
  • 超级块(Super Block):记录下一组空闲块的块号和块数
  • 分配时从第一组中取块,组内块用完时读入下一组的超级块信息
  • 回收时将释放的块加入第一组,第一组满时再写入超级块
  • 优点:兼顾了空间利用率和分配效率,避免维护一个巨大的链表或位图
  • 缺点:频繁的超级块更新操作可能影响性能
指向原始笔记的链接

文件系统实现

文件系统实现

文件系统定义

  • 操作系统中实现文件统一管理的一组软件和相关数据的集合
  • 负责组织、存储、检索、共享和保护文件信息

文件系统功能

  • 实现”按名存取”,将用户提供的文件名映射为物理存储位置
  • 提供方便的操作和统一的调用接口,屏蔽底层存储设备细节
  • 组织、分配、回收文件的存储空间
  • 负责文件的存储、检索、共享和保护

文件系统分层架构

文件系统通常采用层次化设计,各层分工明确:

  1. 用户接口层:提供系统调用接口(open/read/write/close 等)
  2. 目录管理层:解析路径名,定位文件目录项
  3. 访问控制层:验证用户权限,确保安全性
  4. 逻辑文件系统层 + 缓冲区管理层:将用户请求转换为逻辑块号,管理文件缓冲区
  5. 物理文件系统层:将逻辑块号转换为物理块号,与设备驱动交互
  6. 设备驱动层:直接与硬件设备(磁盘、SSD)通信,完成实际的 I/O 操作

分层优点:模块化、易于维护和扩展,各层可独立实现和优化。

传统文件系统调用

创建 / 删除

  • create(filename, attributes)
    • 检查文件是否已存在(遍历目录)
    • 分配一个新的 FCB(inode)
    • 将 FCB 写入磁盘
    • 更新目录项,添加新文件名到父目录
    • 参数:文件名(路径名)、设备名(卷名)、FCB 信息(权限、类型等)
  • delete(filename)
    • 检查文件是否存在及权限
    • 释放文件占用的所有数据块(回收至空闲空间管理)
    • 释放 FCB(inode)
    • 从目录中删除对应的目录项
    • 如果是硬链接且链接数 > 1,只减少链接计数,不删除数据

打开 / 关闭

  • open(filename, flags)
    • 根据文件名搜索目录,找到对应的 FCB
    • 将 FCB 从磁盘读入内存(到系统级打开文件表)
    • 创建文件描述符(fd)或文件句柄,返回给用户进程
    • 建立用户与文件的连接,维护文件的读写位置指针
    • 文件描述符:一个整数索引,指向进程级打开文件表中的表项
  • close(fd)
    • 写回 FCB 到磁盘(更新文件大小、修改时间等)
    • 释放进程级打开文件表中的表项
    • 递减系统级打开文件表中的引用计数,引用计数为 0 时释放并写回 FCB

读 / 写

  • read(fd, buf, count)
    • 根据文件描述符找到对应的打开文件表项
    • 从当前读写位置开始读取 count 字节
    • 通过文件系统的映射机制找到数据所在的磁盘块
    • 将数据读入缓冲区(buf),然后拷贝到用户空间
    • 更新文件读写位置指针
  • write(fd, buf, count)
    • 类似读取,但方向相反
    • 如果当前块写满,需分配新的磁盘块
    • 更新文件大小和修改时间
    • 更新读写位置指针
  • lseek(fd, offset, whence):定位读写位置,实现随机访问

文件描述符与打开文件表

文件描述符是进程和打开文件之间的桥梁。

  • 每进程打开文件表(进程级)
    • 每个进程维护自己的打开文件表
    • 表项包含:文件描述符、指向系统级打开文件表项的指针
    • fork() 时,子进程继承父进程的打开文件表
  • 系统级打开文件表(全局)
    • 整个系统共享一张打开文件表
    • 表项包含:文件读写位置指针、打开模式、引用计数、FCB 指针(指向内存中的 inode)
    • 多个文件描述符可以指向同一个系统级表项(如 dup() 复制文件描述符时)
  • 内存 inode 表
    • 存储从磁盘读入内存的 inode(FCB)
    • 多个系统级表项可共享同一个内存 inode(如硬链接打开的文件)
    • 当引用计数为 0 时,回写并释放

文件缓冲区管理

  • 引入缓冲区的目的:缓解 CPU 与 I/O 设备之间的速度差异,减少物理 I/O 次数
  • 预读(Read-Ahead):程序顺序读取文件时,系统预测性地提前将后续块读入缓存
    • 基于”局部性原理”:如果程序读取第 n 块,很可能接下来读取第 n+1 块
    • 每次读入更大块(如 8KB 或 16KB),减少 I/O 次数
  • 写缓冲(Write Buffer / Delayed Write):写操作先写入内存缓冲区,延迟写入磁盘
    • 避免每次 write() 都触发磁盘 I/O
    • 缓冲区满或显式调用 sync() / fsync() 时真正写入磁盘
    • 缺点:系统崩溃可能导致数据丢失(文件系统日志 / Journaling 可缓解此问题)

UNIX inode(索引节点)

  • inode 是 UNIX/Linux 文件系统的核心数据结构,存储文件的全部元数据(FCB 信息)
  • 文件名不存储在 inode 中,而是存储在目录文件中,使得同一个 inode 可以有多个文件名(硬链接)
  • inode 包含以下关键字段:
    • 文件类型与权限(st_mode
    • 文件链接计数(st_nlink
    • 文件所有者和所属组(st_uid, st_gid
    • 文件大小(st_size
    • 时间戳(st_atime, st_mtime, st_ctime
    • 数据块指针数组(直接块 + 间接块指针)

混合索引结构(UNIX)

UNIX 采用混合索引结构来管理文件数据块的地址,兼顾小文件和大文件:

  • 索引节点中共有 13 个块指针(以传统 UNIX V7 和早期 Linux ext2 为例):
    • 10 个直接块指针:直接指向数据块
    • 1 个单级间接块指针:指向一个索引块,该索引块包含若干数据块指针
    • 1 个二级间接块指针:指向索引块→索引块→数据块
    • 1 个三级间接块指针:指向索引块→索引块→索引块→数据块

最大文件大小计算(以 1KB 块大小为例)

  • 每个索引块(间接块)可存放 1024 / 4 = 256 个块指针(假设指针大小 4 字节)
  • 直接块:10 × 1KB = 10KB
  • 单级间接:256 × 1KB = 256KB
  • 二级间接:256 × 256 × 1KB = 64MB
  • 三级间接:256 × 256 × 256 × 1KB = 16GB
  • 最大文件大小 ≈ 16GB + 64MB + 256KB + 10KB ≈ 16.06GB

不同块大小的最大文件大小:

块大小最大文件大小
1KB~16GB
2KB~256GB
4KB~4TB

(实际 ext4 等现代文件系统使用 extent 树代替了简单的混合索引,支持更大文件)

为什么采用混合索引?

  • 适应不同大小的文件
    • 小文件(< 10KB):只需使用直接块,无需额外索引块,开销极小
    • 中等文件:通过单级或二级间接,无需三级间接,节省索引块
    • 大文件:通过多级间接,理论上支持 TB 级大文件
  • 空间与性能的权衡:多数文件很小(据统计,大部分文件 < 10KB),混合索引为小文件优化
  • 灵活性:文件动态增长时可逐级扩展索引结构,不需要预先分配

内存映射文件(Memory-Mapped File)

  • 另一种文件访问接口
  • 将文件的一部分或全部映射到进程的虚拟地址空间
  • 进程通过指针直接读写内存,操作系统自动将修改写回文件
  • 优点:无需 read/write 系统调用开销,简化编程,数据共享方便(多个进程映射同一文件)
  • 缺点:不适合超大文件(地址空间有限),映射区域大小需与页大小对齐

文件系统的组成

  • 卷管理:管理磁盘分区(卷),跟踪分区信息和超级块
  • 目录管理:维护目录层次结构,支持路径解析和目录操作
  • 文件空间管理:分配和回收磁盘块(空闲空间管理)
  • 文件操作实现:实现 create/delete/open/close/read/write 等系统调用
  • 文件保护与安全:访问控制、权限验证、加密等
指向原始笔记的链接

文件保护与安全

文件保护与安全

访问控制

  • 每个文件关联访问控制信息,确定用户对文件的访问权限
  • 文件系统需要验证每次操作是否合法,防止未授权访问

保护机制

口令保护

  • 为每个文件设置一个口令(password)
  • 用户访问文件时必须提供正确的口令
  • 优点:实现简单,开销小
  • 缺点:口令存储在文件系统中可能被窃取,权限粒度粗(一个口令对所有操作),修改口令需通知所有用户

加密保护

  • 对文件内容进行加密存储,即使数据被窃取也无法解读
  • 用户访问时需要提供密钥进行解密
  • 优点:安全性高,即使存储介质被盗也无法读取
  • 缺点:加解密有计算开销;密钥管理复杂(丢失密钥则数据永久丢失)

访问控制矩阵

  • 用二维矩阵表示系统中的访问控制信息
  • :系统中的主体(用户/进程)
  • :系统中的客体(文件/设备等资源)
  • 矩阵元素:主体对客体拥有的访问权限
  • 优点:模型清晰,理论上可表达任意访问策略
  • 缺点:矩阵稀疏(大多数用户对大多数文件无权限),存储开销大,实际系统中很少直接使用
  • 实际实现:分解为按行(Capability List)或按列(ACL)存储

访问控制列表(ACL)

  • 按列分解:每个文件关联一个列表,记录哪些用户(或组)拥有何种权限
  • 每次访问时,系统根据发起操作的用户身份在 ACL 中查找对应权限
  • 优点:逻辑清晰,易于管理和修改(仅修改文件本身的 ACL)
  • 缺点:用户数量大时 ACL 变长,查找效率下降
  • UNIX 简化 ACL:将用户分为三类——所有者(owner)、同组用户(group)、其他用户(others),各用 3 位(rwx)表示权限
    • 这是 ACL 的一种简化形式,用 9 位权限位替代完整 ACL 列表
    • 效率高,但粒度粗(无法为单个用户单独设权)

访问权限类型

  • 读(R):查看文件内容
  • 写(W):修改文件内容
  • 执行(X):运行可执行文件(对目录而言表示可进入该目录)
  • 删除(D):删除文件
  • 修改属性:修改文件属主、权限等元数据
  • 遍历文件夹:对目录的访问权限(列出目录内容)
  • 在 UNIX 中,对目录的读/写/执行含义与普通文件不同:
    • 目录的 R:可列出目录内容(ls
    • 目录的 W:可在目录中创建或删除文件
    • 目录的 X:可进入目录(cd),但未必能列出内容

文件共享机制

  • 多个文件名指向同一个 inode(索引节点)
  • 实质:在目录中创建新的目录项,指向同一个 inode
  • 特点:
    • 所有硬链接地位平等,没有主次之分,全部删除后文件才真正被删除
    • inode 中维护链接计数(st_nlink),每创建一个硬链接计数 +1,删除一个时 -1,计数为 0 时释放 inode 和数据块
    • 硬链接不能跨文件系统(inode 号仅在同一个文件系统内唯一)
    • 不能对目录创建硬链接(防止环路)
  • 优点:共享效率高(无额外空间开销),共享关系对应用程序透明
  • 缺点:删除时容易误以为文件已删除(实际还存在其他链接),逻辑复杂
  • 创建一个特殊类型文件,其内容存储指向目标文件的路径名
  • 访问软链接时,系统自动读取该文件中的路径,重定向到目标文件
  • 特点:
    • 软链接有自己的 inode,与原文件 inode 不同
    • 可以跨文件系统(因为存的是路径名,而非 inode 号)
    • 可以对目录创建软链接
    • 删除原文件后,软链接仍然存在,但指向无效路径——悬空链接(Dangling Link)
    • 符号链接文件本身的权限(lrwxrwxrwx)通常无实际意义,权限由目标文件决定
  • 优点:灵活,支持跨文件系统共享
  • 缺点:访问时需多一次路径解析,存在悬空链接风险

硬链接 vs 软链接对比

特性硬链接软链接
本质多个目录项指向同一 inode存路径名的特殊文件
跨文件系统不支持支持
目录共享不支持支持
链接目标删除后文件仍可通过其他链接访问链接失效(悬空)
对应用透明性透明有间接性
inode共用各自独立

文件备份

备份策略

  • 全量备份(Full Backup):复制所有文件到备份介质
    • 优点:恢复最简单,只需一份备份即可完全恢复
    • 缺点:备份时间长,占用存储空间大
    • 通常作为周期性基准备份(如每周日执行)
  • 增量备份(Incremental Backup):只备份自上次任何类型备份以来修改过的文件
    • 优点:备份量小,速度快,节省存储空间
    • 缺点:恢复时需要全量备份 + 所有增量备份链,任一个增量损坏则后续数据丢失
    • 通常每日执行
  • 差异备份(Differential Backup):只备份自上次全量备份以来修改过的文件
    • 优点:恢复比增量快(只需全量 + 最新差异备份)
    • 缺点:每次差异备份逐渐变大(因为累积所有自全量后的变化)
    • 在备份速度与恢复速度之间折中

备份示例(周日全量 + 周一到周六增量/差异)

日期修改文件全量增量差异
周日-全量 AAA
周一f1-f1f1
周二f2-f2f1,f2
周三f3-f3f1,f2,f3
周四恢复-需 A+f1+f2+f3需 A+f1+f2+f3

差异备份周三需备份 f1+f2+f3(累积),增量只需 f3(仅当日变化)。

备份注意事项

  • 定期执行:按业务需求设定备份周期(如每日增量 + 每周全量)
  • 异地存储:备份介质最好存放在异地,防止火灾、盗窃等灾难导致主备同时丢失
  • 恢复测试:定期测试备份的可恢复性,确保备份数据可用
  • 冷热备份:热备份(系统运行中备份)vs 冷备份(停机备份),热备份需解决一致性问题
指向原始笔记的链接