文件管理负责组织和管理存储设备上的数据,为用户提供按名存取的文件操作接口。核心任务包括文件逻辑结构与物理结构的设计、目录组织与管理、磁盘空间分配与回收、文件共享与保护机制,确保数据存储的可靠性、安全性与高效性。
文件与目录
文件与目录
文件
- 文件是操作系统中最为常见的对象
- 有名称的一组相关信息的集合
- 文件是信息的一种抽象机制,提供将信息保存在外存并方便读取的手段
文件结构
逻辑结构(用户视角)
- 流式文件(字节流):文件由字节序列组成,无内部结构,如
.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.txt或docs/report.txt- 优点:层次清晰,支持文件分组,不同目录下可包含同名文件
- 缺点:共享文件需通过路径链接
无环图目录
- 在树形目录基础上增加共享链接,允许多个路径指向同一文件
- 支持文件共享(通过硬链接或符号链接实现)
- 需将 FCB 与文件内容分离:FCB 中存放文件物理位置,目录项中存放文件名和 FCB 指针
- 多个目录项可指向同一个 FCB,实现共享
- 注意:即使从某个目录中删除该文件,只要还有其他链接,文件内容就不应删除(引用计数机制)
文件存储空间管理
- 操作系统需要跟踪磁盘上空闲块,分配新块给文件,回收删除文件释放的块
空闲表法
- 系统维护一张空闲表,记录每个连续空闲区的起始块号和块数
- 适用于连续结构文件分配
空闲链表法
- 将所有空闲块链接成一个链表,分配时从链头取块,回收时挂回链尾
- 空闲盘块链:以数据块为单位链接
- 空闲盘区链:以连续空闲区为单位链接
位图法(Bit Map)
- 用一个二进制位(bit)对应一个物理块,0 表示空闲,1 表示已占用
- 优点:查找空闲块效率高(利用 CPU 位操作指令),占用空间小
- 缺点:位图需全部调入内存才能工作(大容量磁盘下位图较大)
- 现代文件系统(如 ext 系列)广泛采用位图管理
成组链接法(UNIX 系统)
指向原始笔记的链接
- 将空闲块分组管理,每 50 个空闲块为一组
- 超级块(Super Block):记录下一组空闲块的块号和块数
- 分配时从第一组中取块,组内块用完时读入下一组的超级块信息
- 回收时将释放的块加入第一组,第一组满时再写入超级块
- 优点:兼顾了空间利用率和分配效率,避免维护一个巨大的链表或位图
- 缺点:频繁的超级块更新操作可能影响性能
文件系统实现
文件系统实现
文件系统定义
- 操作系统中实现文件统一管理的一组软件和相关数据的集合
- 负责组织、存储、检索、共享和保护文件信息
文件系统功能
- 实现”按名存取”,将用户提供的文件名映射为物理存储位置
- 提供方便的操作和统一的调用接口,屏蔽底层存储设备细节
- 组织、分配、回收文件的存储空间
- 负责文件的存储、检索、共享和保护
文件系统分层架构
文件系统通常采用层次化设计,各层分工明确:
- 用户接口层:提供系统调用接口(open/read/write/close 等)
- 目录管理层:解析路径名,定位文件目录项
- 访问控制层:验证用户权限,确保安全性
- 逻辑文件系统层 + 缓冲区管理层:将用户请求转换为逻辑块号,管理文件缓冲区
- 物理文件系统层:将逻辑块号转换为物理块号,与设备驱动交互
- 设备驱动层:直接与硬件设备(磁盘、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),但未必能列出内容文件共享机制
硬链接(Hard Link)
- 多个文件名指向同一个 inode(索引节点)
- 实质:在目录中创建新的目录项,指向同一个 inode
- 特点:
- 所有硬链接地位平等,没有主次之分,全部删除后文件才真正被删除
- inode 中维护链接计数(
st_nlink),每创建一个硬链接计数 +1,删除一个时 -1,计数为 0 时释放 inode 和数据块- 硬链接不能跨文件系统(inode 号仅在同一个文件系统内唯一)
- 不能对目录创建硬链接(防止环路)
- 优点:共享效率高(无额外空间开销),共享关系对应用程序透明
- 缺点:删除时容易误以为文件已删除(实际还存在其他链接),逻辑复杂
软链接 / 符号链接(Symbolic Link)
- 创建一个特殊类型文件,其内容存储指向目标文件的路径名
- 访问软链接时,系统自动读取该文件中的路径,重定向到目标文件
- 特点:
- 软链接有自己的 inode,与原文件 inode 不同
- 可以跨文件系统(因为存的是路径名,而非 inode 号)
- 可以对目录创建软链接
- 删除原文件后,软链接仍然存在,但指向无效路径——悬空链接(Dangling Link)
- 符号链接文件本身的权限(
lrwxrwxrwx)通常无实际意义,权限由目标文件决定- 优点:灵活,支持跨文件系统共享
- 缺点:访问时需多一次路径解析,存在悬空链接风险
硬链接 vs 软链接对比
特性 硬链接 软链接 本质 多个目录项指向同一 inode 存路径名的特殊文件 跨文件系统 不支持 支持 目录共享 不支持 支持 链接目标删除后 文件仍可通过其他链接访问 链接失效(悬空) 对应用透明性 透明 有间接性 inode 共用 各自独立 文件备份
备份策略
- 全量备份(Full Backup):复制所有文件到备份介质
- 优点:恢复最简单,只需一份备份即可完全恢复
- 缺点:备份时间长,占用存储空间大
- 通常作为周期性基准备份(如每周日执行)
- 增量备份(Incremental Backup):只备份自上次任何类型备份以来修改过的文件
- 优点:备份量小,速度快,节省存储空间
- 缺点:恢复时需要全量备份 + 所有增量备份链,任一个增量损坏则后续数据丢失
- 通常每日执行
- 差异备份(Differential Backup):只备份自上次全量备份以来修改过的文件
- 优点:恢复比增量快(只需全量 + 最新差异备份)
- 缺点:每次差异备份逐渐变大(因为累积所有自全量后的变化)
- 在备份速度与恢复速度之间折中
备份示例(周日全量 + 周一到周六增量/差异)
日期 修改文件 全量 增量 差异 周日 - 全量 A A A 周一 f1 - f1 f1 周二 f2 - f2 f1,f2 周三 f3 - f3 f1,f2,f3 周四恢复 - 需 A+f1+f2+f3 需 A+f1+f2+f3 差异备份周三需备份 f1+f2+f3(累积),增量只需 f3(仅当日变化)。
备份注意事项
指向原始笔记的链接
- 定期执行:按业务需求设定备份周期(如每日增量 + 每周全量)
- 异地存储:备份介质最好存放在异地,防止火灾、盗窃等灾难导致主备同时丢失
- 恢复测试:定期测试备份的可恢复性,确保备份数据可用
- 冷热备份:热备份(系统运行中备份)vs 冷备份(停机备份),热备份需解决一致性问题