回顾:
1. 设备固有特性带来的挑战
- 由于寻道时间、旋转延迟、传输时间(硬盘驱动器)造成的延迟
- 用于写页的块擦除(SSDs)
2. 两级性能提升:
- 磁盘调度和柱面斜进
- 文件系统实现
本章小结:
- 文件系统层与磁盘布局
- 文件、目录及操作系统数据结构的实现
- 可用空间管理、分区、引导扇区等
文件系统
操作系统能为我做什么?
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class Demo1 {
public static void main(String[] args) throws IOException {
FileWriter fw =
new FileWriter(("C:/Program Files (x86)/test.txt"));
PrintWriter pw = new PrintWriter(fw);
pw.close();
}
}
-
导入必要的类:
FileWriter、IOException、PrintWriter。 -
主方法:声明可能抛出
IOException。 -
创建FileWriter对象:尝试在路径
"C:/Program Files (x86)/test.txt"创建文件 -
创建PrintWriter对象:包装FileWriter以便更方便地写入文本。
-
关闭资源:调用
pw.close(),这会自动关闭底层的FileWriter。
文件系统抽象:将逻辑文件系统映射到物理文件系统(从物理层抽象)
从设备抽象:统一的观点,非常不同的底层存储机制
并发性:如果多个进程同时访问文件怎么办
安全性:为什么拒绝访问
文件系统能够使数据易于存储、定位和检索,且效率高。
磁盘布局
引导(Boot)扇区和分区
磁盘由一系列扇区组成(0 - N)
引导记录(Boot record)位于磁盘的起始位置:
- 用于启动计算机(BIOS 读取并执行引导扇区)
- 其末尾包含分区表,其中包含活动分区
- 一个分区被标记为活动分区,其中包含加载操作系统的引导块
磁盘通常被分成多个分区:
- 每个分区可能包含不同的文件/操作系统(有时甚至没有)

分区布局(取决于文件系统)
包含启动操作系统所需代码的引导块 Boot block(每个分区都包含,无论该分区是否包含操作系统)
包含有关分区信息的超级块 Super block(分区大小、FCB(文件控制块)数量、空闲列表位置等)
空闲空间管理包含指示空闲 FCB(文件控制块)或数据块的数据结构
元数据或文件控制块(例如 i 索引节点)
数据块,包括根目录(文件系统树的顶部)
文件&目录
操作系统抽象
一种用户视角,通过操作系统提供的抽象(系统调用)来定义文件系统(文件和目录)
一种实现视角,通过文件系统的底层实现来定义其内容
逻辑层(Logical Layers)
共享层:
- 输入/输出控制(I/O control)与设备控制器/寄存器(device controller/registers)(设备驱动程序、中断处理程序)进行交互
- 基本文件系统(Basic file system)指示设备驱动程序“块”、安排输入/输出操作,并管理(元)数据的缓冲区和缓存
特定于文件系统的层:
- 文件组织(File organisation)为文件和可用空间划分逻辑块
- 逻辑文件系统(Logical file system)管理文件控制块、目录结构和保护
应用程序(Application programs)定义文件的结构
文件(Files)
类型
Windows 和 Unix(包括 OS X)都具有常规文件和目录:
- 常规文件以 ASCII 或二进制(定义明确)格式存储用户数据
- 目录将文件组合在一起(但从实现层面来看,它们也是文件)
Unix 还有字符特殊文件和块特殊文件:
- 字符特殊文件用于模拟串行 I/O 设备(例如键盘、打印机)
- 块特殊文件用于模拟,例如硬盘
文件是顺序访问、随机(直接)访问和索引访问的
文件控制块与表
文件控制块(File control blocks,FCBs)是内核数据结构
- 如果允许用户应用程序直接访问这些控制块,可能会破坏其完整性
- 系统调用使用户应用程序能够(在内核模式下)请求操作系统为其执行操作
FCB 保存在每个进程和系统范围的打开文件表(数组)中,通过进程特定的文件句柄进行索引
每个进程的文件表包含了与该进程相关的特定信息,例如:
- 该进程当前打开的所有文件
- 读/写/当前指针
- 指向系统级文件表中相关条目的引用
系统级文件表包含一般信息,例如:
- 每个打开的文件对应一个条目
- 磁盘上的位置
- 访问时间
- 引用计数
系统调用
用于文件操作的系统调用包括:创建(create())、打开(open())、关闭(close())、读取(read())、写入(write())等等。例如:
open() 系统调用:
- 将逻辑名称映射到标识文件控制块的底层名称
- 从驱动器中获取“FCB”(文件控制块)
- 将其添加到进程/系统打开文件表中(增加引用计数)
- 返回进程特定的文件句柄(表中的索引)
close() 系统调用:
- 减少引用计数
- 与磁盘同步 FCB
- 当引用计数为 0 时,从进程/系统文件表中移除 FCB
示例 使用“strace”(在 MacOS 系统中称为“dtruss”):
# command = strace cat helloWorld.txt > /dev/null
execve("/usr/bin/cat", ["cat", "helloWorld.txt"], 0x7fffccb21658 /* 34 vars */) = 0
...
open("helloWorld.txt", O_RDONLY) = 3
...
read(3, "Hello World\n", 1048576) = 12
write(1, "Hello World\n", 12) = 12
read(3, "", 1048576) = 0 # 再次尝试读取,返回 0 字节(文件结束)
...
close(3) = 0 # 关闭数据文件
close(1) = 0 # 关闭标准输出(/dev/null)
close(2) = 0 # 关闭标准错误
exit_group(0) = ? # 进程以状态码 0(成功)退出
-
strace cat helloWorld.txt > /dev/null:使用 strace 跟踪 cat 命令的系统调用 -
> /dev/null:将标准输出重定向到空设备(丢弃输出)
execve - 执行程序:
-
加载并执行
/usr/bin/cat程序 -
传递参数:
"cat"和"helloWorld.txt" -
继承 34 个环境变量
-
返回 0 表示成功
open - 打开文件:
-
以只读模式打开
helloWorld.txt -
返回文件描述符 3(0-2 是标准输入、输出、错误)
read - 读取文件内容:
-
从文件描述符 3 读取数据
-
读取到内容:
"Hello World\n"(12 个字节) -
缓冲区大小为 1048576 字节(1MB)
-
返回实际读取的字节数:12
write - 写入输出
-
向文件描述符 1(标准输出)写入 12 字节
-
虽然输出被重定向到
/dev/null,但 write 调用仍然执行 -
返回写入的字节数:12
目录(Directories)
实现
目录是将文件进行分类的特殊文件,其结构由文件系统定义。
- 一个bit会被设置以表明它们是目录。
- 它们将人类可读的“逻辑”名称映射到用于文件控制块的唯一标识符,这些标识符详细说明了物理位置和文件属性。
有两种方法:
- 所有属性都存储在目录文件中(例如文件名、磁盘地址——Windows)
- 指向包含文件属性的数据结构(例如 i-节点)的指针(Unix)


目录能够构建有向无环图(directed acyclic-graphs,DAG)(是对树形结构的扩展——但链接可能会破坏这种结构)
系统调用
与文件类似,目录也是通过系统调用来进行操作的。
- create/delete:创建/删除一个新目录
- opendir、closedir:将目录添加/从内部表中移出
- readdir:返回目录文件中的下一个条目
- 其他操作:rename, link, unlink, list, update
常见的操作包括创建、删除、搜索、列出、遍历等等。
例子:文件访问,读取/home/pszgd/COMP2007/helloWorld.txt
#Steps to read /home/pszgd/COMP2007/helloWorld.txt
- read FCB for /
- find FCB location for /home
- read FCB for /home
- find FCB location of /home/pszgd
- read FCB for /home/pszgd
- find FCB location for /home/pszgd/COMP2007
- read FCB for /home/pszgd/COMP2007
- find FCB location for /home/pszgd/COMP2007/helloWorld.txt
- read FCB /home/pszgd/COMP2007/helloWorld.txt
=> update per process/system file tables
- read data for /home/pszgd/COMP2007/helloWorld.txt
- close the file
=> update per process/system file tables
(last access times may need updating on disk)
检索文件归根结底就是要尽可能快速地在目录文件中进行搜索
简单的随机排列目录项可能不够(搜索时间与项的数量呈线性关系)
对于大型目录,可以使用索引或哈希表来实现
空闲空间管理
与内存管理类似,位图和链表也可用于空闲空间管理
位图(Bitmap)
位图通过在映射表中单个位来表示每个块
- 位图的大小会随着磁盘大小的增加而增加,但对于给定的磁盘而言则是恒定的
- 位图所占用的空间比链表相对更少
链表(Linked List)
链表中的自由组集合
- 利用空闲块来保存空闲块的位置(因此,它们不再是空闲的)
- 链表的大小会随着磁盘的大小而增加,随着块的大小而缩小 ①例如,对于 1KB 的块和 32 位/4 字节的磁盘块编号,每个块将容纳 255 个空闲块(一个用于指向下一个块的指针)②由于当磁盘满时自由列表会缩小,所以这并非浪费的空间
- 块是相互链接的,即多个块列出了可用的块
链表可以通过跟踪每个条目连续空闲块的数量(称为Counting)来进行修改
位图 vs. 链表

位图:
- 需要额外的存储空间。例如:如果块大小为
字节(4KB),而磁盘大小为
字节(1GB)⇒ 位图大小:
/
=
(32KB)
- 仅对于小型磁盘才有可能将其保留在内存中。
链表:
- 随着空块数量的增加而增长
- 不会浪费磁盘空间(利用空闲空间)
- 我们只需在内存中保存一个指向块的指针块(需要时加载新的块)。
604

被折叠的 条评论
为什么被折叠?



