前言
文件系统提供六种基本操作:创建、删除、打开、关闭、读、写。其他复杂操作均由这些基本操作组合完成。
1. 创建文件
系统调用参数:所需外存空间大小、文件存放路径、文件名。
系统做的两件事:
- 根据申请的大小,用存储空间管理策略(空闲表法/位视图法等)在外存中分配磁盘块;
- 在指定路径的目录表中插入一条新的目录项(FCB),记录文件名、存放位置等信息。
2. 删除文件
系统调用参数:文件存放路径、文件名。
系统做的三件事:
- 根据路径找到对应目录表,再按文件名找到目录项,读取文件占用的磁盘块信息;
- 按存储空间管理策略回收文件占用的磁盘块;
- 删除目录表中该文件的目录项。
3. 打开文件
在大多数操作系统中,对文件进行读写之前必须先调用 open。
系统调用参数:文件存放路径、文件名、操作类型(只读 / 读写等)。
系统做的两件事:
- 根据路径在外存中找到目录表,检查用户的访问权限(权限信息记录在目录项中),若无权限则拒绝;
- 权限合法后,将目录项复制到内存的打开文件表中,并将该表项的索引号(文件描述符)返回给用户。
打开文件时不会将文件的实际数据读入内存,只是将目录项信息载入打开文件表。
打开文件表
内存中存在两张打开文件表:
| 系统打开文件表 | 进程打开文件表 | |
|---|---|---|
| 数量 | 全局唯一一张 | 每个进程一张 |
| 记录内容 | 所有正在被进程使用的文件信息 | 该进程已打开的文件信息 |
| 特有字段 | 打开计数器(记录当前有几个进程打开了该文件) | 读写指针(记录当前读写进行到的位置)、访问权限(该进程打开时申请的操作类型) |
进程打开文件表中包含一个指向系统表对应表项的索引号,多个进程打开同一文件时各自持有独立的读写指针和权限,但共享系统表中的同一条记录。
打开计数器的作用:系统在删除文件前会先查系统打开文件表,若打开计数器 > 0(有进程正在使用),则拒绝删除(即 Windows 中「文件正在被占用,无法删除」的情形)。
4. 关闭文件
系统做的三件事:
- 删除进程打开文件表中该文件对应的表项,回收相关内存缓冲区等资源;
- 将系统打开文件表中对应表项的打开计数器减 1;
- 若打开计数器减为 0,说明无进程再使用该文件,则删除系统打开文件表中的该表项。
5. 读文件
读写操作必须在文件已打开的前提下进行。
系统调用参数:文件描述符(打开文件表索引号)、读入数据量(如 1 KB)、目标内存地址。
系统操作:根据进程打开文件表中的读写指针找到外存对应位置,将指定大小的数据读入用户指定的内存区域,并更新读写指针。
只有 read 操作才会将文件的实际数据从外存读入内存。
6. 写文件
系统调用参数:文件描述符、写回数据量(如 1 KB)、源内存地址。
系统操作:从用户指定的内存区域读取指定大小的数据,写回到写指针所指向的外存位置,并更新写指针。
写文件与读文件流程对称,二者操作参数结构完全相同。
7. 小结
| 操作 | 是否访问外存目录 | 是否将文件数据读入内存 | 关键点 |
|---|---|---|---|
| 创建 | 写目录 | 否 | 分配磁盘块 + 插入目录项 |
| 删除 | 读+删目录 | 否 | 回收磁盘块 + 删除目录项 |
| 打开 | 读目录 | 否(仅复制目录项到打开文件表) | 权限检查;返回文件描述符 |
| 关闭 | 否 | 否 | 打开计数器 -1;归零时删系统表项 |
| 读 | 否(查打开文件表) | 是 | 依据读写指针定位外存数据 |
| 写 | 否(查打开文件表) | 否(内存→外存) | 依据写指针写回外存 |
最容易混淆的点:
- 打开文件 ≠ 读文件:open 只把目录项复制到内存打开文件表,不读取文件数据;read 才真正将文件数据从外存读入内存。
- 读写操作时用户只需提供文件描述符(打开文件表索引号),无需再提供文件名或路径。
- 文件描述符即进程打开文件表的索引号(序号),也称为"文件句柄"。