30 KiB
17 I/O系统
一、I/O系统概述
1.1 I/O系统的基本概念
I/O系统(输入/输出系统)是操作系统中负责管理外部设备与**主机(CPU和内存)**之间数据传输的子系统。它是计算机系统中最复杂的组成部分之一。
核心任务:屏蔽各种I/O设备的硬件差异,为上层提供统一、简洁的I/O接口,使用户程序能够方便地使用各种外部设备。
I/O系统需要解决的关键问题:
- 设备的多样性与统一管理
- CPU与I/O设备之间的速度差异
- 设备的分配与回收
- I/O操作的高效执行
1.2 I/O设备分类
按数据传输单位分类
| 类型 | 特征 | 典型设备 |
|---|---|---|
| 字符设备 | 以字符为单位传输,速率低,不可寻址 | 键盘、鼠标、串口 |
| 块设备 | 以数据块为单位传输,速率高,可寻址 | 磁盘、磁带、光盘 |
按传输速率分类
| 类型 | 速率范围 | 典型设备 |
|---|---|---|
| 低速设备 | 几字节/s ~ 几百B/s | 鼠标、键盘、调制解调器 |
| 中速设备 | 几KB/s ~ 几MB/s | 打印机、扫描仪 |
| 高速设备 | 几MB/s ~ 几GB/s | 磁盘、磁带、网络接口 |
按使用特性分类
| 类型 | 说明 | 典型设备 |
|---|---|---|
| 存储设备 | 用于永久保存信息 | 磁盘、磁带、光盘 |
| 输入设备 | 用于向计算机输入信息 | 键盘、鼠标、扫描仪 |
| 输出设备 | 用于将计算机信息输出 | 显示器、打印机 |
| 交互设备 | 既能输入又能输出 | 触摸屏、网络接口 |
1.3 I/O设备的组成
I/O设备一般由机械部分和电子部分组成:
I/O设备 = 设备(Device)+ 设备控制器(Device Controller)
- 设备(Device):执行实际的I/O操作(如磁盘的旋转和读写)
- 设备控制器(Device Controller):电子部件,负责控制设备的物理操作,是CPU与设备之间的接口
设备控制器的功能:
- 接收和识别CPU发来的命令
- 实现CPU与控制器、控制器与设备之间的数据交换
- 记录设备的状态供CPU查询
- 识别设备地址(端口地址)
二、I/O控制方式
I/O控制方式的发展目标:减少CPU对I/O过程的干预,提高CPU与I/O设备的并行操作程度。
2.1 程序直接控制方式(Programmed I/O)
基本思想
CPU通过不断**轮询(polling)**设备状态寄存器,直接控制I/O操作的全过程。
工作流程
sequenceDiagram
participant CPU
participant 控制器
participant 设备
CPU->>控制器: 1. 发送I/O命令
CPU->>控制器: 2. 读取状态寄存器
loop 轮询等待
CPU->>控制器: 检查忙/完成位
控制器-->>CPU: 返回状态(忙)
end
控制器-->>CPU: 返回状态(完成)
CPU->>控制器: 3. 读取数据寄存器
控制器-->>CPU: 数据
优缺点
| 优点 | 缺点 |
|---|---|
| 实现简单 | CPU利用率极低(忙等待) |
| 硬件要求低 | CPU与设备串行工作 |
| 易于理解 | 不适用于多设备系统 |
[!note] 关键特征 CPU在整个I/O过程中一直参与,以轮询方式检查设备状态,CPU与设备完全串行工作。
2.2 中断驱动方式(Interrupt-Driven I/O)
基本思想
CPU发出I/O命令后,不需要持续轮询,可以去执行其他进程。当设备完成操作后,通过中断通知CPU。
工作流程
sequenceDiagram
participant CPU
participant 控制器
participant 设备
CPU->>控制器: 1. 发送I/O命令
Note over CPU: CPU转去执行其他进程
控制器->>设备: 启动设备操作
设备->>控制器: 操作完成
控制器->>CPU: 2. 发出中断请求
Note over CPU: CPU响应中断
CPU->>控制器: 3. 读取数据
控制器-->>CPU: 数据
优缺点
| 优点 | 缺点 |
|---|---|
| CPU利用率提高 | 每次传输以字/字节为单位 |
| CPU与设备可并行工作 | 频繁中断开销大 |
| 支持多设备 | 数据仍需经过CPU中转 |
[!note] 与程序直接控制方式的对比 中断驱动方式中,CPU在启动I/O后可执行其他任务,设备完成时通过中断通知CPU,实现了CPU与设备的部分并行。但每次数据传输都需要CPU干预。
2.3 DMA方式(Direct Memory Access)
基本思想
引入DMA控制器,在设备与内存之间开辟直接数据通路,数据传输不需要经过CPU中转。CPU只需在传输开始和结束时介入。
DMA控制器的组成
DMA控制器寄存器:
┌─────────────────────────────────┐
│ MAR(内存地址寄存器) │ → 数据在内存中的目标地址
│ DC (数据计数器) │ → 剩余要传输的字节数
│ DR (数据寄存器) │ → 暂存传输的数据
│ CR (命令/状态寄存器) │ → 存放CPU发来的命令和状态
└─────────────────────────────────┘
工作流程
sequenceDiagram
participant CPU
participant DMA
participant 内存
participant 设备
CPU->>DMA: 1. 设置MAR、DC、CR(发送命令)
Note over CPU: CPU转去执行其他进程
DMA->>设备: 启动设备
loop 块传输(硬件自动)
设备->>DMA: 读入一个字
DMA->>内存: 写入内存(MAR)
Note over DMA: MAR++, DC--
end
DMA->>CPU: 2. 传输完成,发出中断
CPU->>DMA: 3. 处理完成中断
DMA与中断驱动的对比
| 比较项 | 中断驱动 | DMA |
|---|---|---|
| 数据传输单位 | 字/字节 | 数据块 |
| 数据是否经过CPU | 是 | 否(直接内存访问) |
| 中断频率 | 每个字/字节一次 | 每块一次 |
| CPU干预程度 | 每次传输都干预 | 仅开始和结束干预 |
[!important] DMA的核心优势 数据在设备和内存之间直接传输,不经过CPU,以块为单位传输,大大减少了CPU的干预次数。
2.4 通道方式(Channel I/O)
基本思想
通道是一种专用处理器(I/O处理器),有自己的指令系统(通道指令)和程序(通道程序)。CPU只需发出一条I/O指令,通道就能自主完成整个数据传输过程。
通道的类型
| 类型 | 特征 | 传输单位 |
|---|---|---|
| 字节多路通道 | 以字节为单位交叉传输多个设备 | 字节 |
| 数组选择通道 | 一次只服务一个设备,传输速率高 | 数据块 |
| 数组多路通道 | 上述两种的结合,兼顾效率和利用率 | 数据块 |
工作流程
sequenceDiagram
participant CPU
participant 通道
participant 控制器
participant 设备
CPU->>通道: 1. 发送通道程序地址
Note over CPU: CPU完全转去执行其他进程
通道->>通道: 取通道指令执行
通道->>控制器: 控制设备操作
loop 通道自主执行
通道->>内存: 数据传输(无需CPU干预)
end
通道->>CPU: 2. 传输完成,发出中断
通道与DMA的对比
| 比较项 | DMA | 通道 |
|---|---|---|
| 控制能力 | 受CPU控制 | 自主执行通道程序 |
| 并行度 | 一个DMA对应一类设备 | 一个通道可控制多个设备 |
| CPU干预 | 需要CPU设置传输参数 | 仅需CPU启动通道 |
| 灵活性 | 较低 | 高(可编程) |
[!tip] I/O控制方式演进总结
方式 CPU干预频率 数据单位 并行程度 程序直接控制 每个字 字 无并行 中断驱动 每个字 字 部分并行 DMA 每个块 块 较高并行 通道 仅启动/结束 一组块 高度并行
三、I/O软件层次结构
3.1 层次结构概览
I/O软件采用分层设计思想,每一层利用下层提供的服务,为上层提供更高级的服务。
graph TB
subgraph 用户空间
A["用户层I/O软件<br/>(库函数: printf, scanf)"]
end
subgraph 内核空间
B["设备独立性软件<br/>(统一接口、缓冲、分配)"]
C["设备驱动程序<br/>(控制具体设备)"]
D["中断处理程序<br/>(响应设备中断)"]
end
subgraph 硬件
E["设备控制器 / 设备"]
end
A -->|"系统调用"| B
B -->|"调用驱动"| C
C -->|"启动I/O"| E
E -->|"中断"| D
D -->|"唤醒驱动"| C
C -->|"唤醒上层"| B
B -->|"返回结果"| A
style A fill:#e1f5fe
style B fill:#fff3e0
style C fill:#fce4ec
style D fill:#f3e5f5
style E fill:#e8f5e9
3.2 各层功能详解
第一层:中断处理程序
位置:最底层,直接与硬件交互 功能:
- 响应设备中断请求
- 保存被中断进程的现场
- 分析中断原因,转入相应的中断处理
- 唤醒等待I/O完成的进程
- 恢复现场,返回被中断的进程
// 中断处理程序伪代码
void disk_interrupt_handler() {
save_context(); // 保存现场
wake_up(device_waiter); // 唤醒等待该设备的进程
schedule(); // 可能触发进程调度
restore_context(); // 恢复现场
}
第二层:设备驱动程序
位置:紧邻中断处理程序之上 功能:
- 将上层的抽象I/O请求转换为对具体设备控制器的操作
- 设置设备控制器的寄存器
- 启动设备进行I/O操作
- 处理设备中断,检查操作结果
[!note] 设备驱动程序的特点
- 每类设备对应一个设备驱动程序
- 是操作系统中了解设备控制器细节的唯一模块
- 通常由设备制造商提供
第三层:设备独立性软件
位置:设备驱动程序之上 功能:
- 提供统一的I/O接口,屏蔽设备差异
- 实现缓冲管理(缓冲区的分配与回收)
- 实现设备分配与回收
- 实现逻辑设备名到物理设备名的映射
- 提供差错处理机制
- 提供设备保护(防止非法访问)
设备独立性:应用程序使用逻辑设备名请求I/O,由系统将其映射到物理设备。这样,更换物理设备不影响程序运行。
应用程序: read(fd, buf, size)
↓ 逻辑设备名
设备独立性软件: 查逻辑设备表 → 物理设备
↓
设备驱动程序: 操作具体设备控制器
第四层:用户层I/O软件
位置:最上层,用户空间 功能:
- 提供库函数接口(如C语言的
printf、scanf、read、write) - 对用户数据进行格式化处理
- 通过系统调用进入内核
3.3 一次完整的I/O操作流程
用户调用 printf("Hello")
→ 用户层:格式化数据,调用 write() 系统调用
→ 设备独立性软件:查设备表,分配缓冲区,确定物理设备
→ 设备驱动程序:向设备控制器发送命令
→ 设备控制器:启动设备进行输出
→ 设备完成操作,发出中断
→ 中断处理程序:唤醒等待进程
→ 设备驱动程序:检查操作结果
→ 设备独立性软件:释放缓冲区,返回结果
→ 用户层:系统调用返回
用户程序继续执行
四、缓冲管理
4.1 引入缓冲的原因
- 缓和CPU与I/O设备速度不匹配的矛盾
- 减少对CPU的中断频率,放宽对中断响应时间的限制
- 提高CPU与I/O设备之间的并行性
4.2 单缓冲(Single Buffer)
基本思想
系统为设备分配一个缓冲区。
工作方式
处理一块数据的时间 = max(C, T) + M
- C:CPU处理一块数据的时间
- T:设备将一块数据传送到缓冲区的时间
- M:缓冲区数据传送到用户区的时间
设备 → [缓冲区] → 用户区 → CPU处理
分析:当C > T时,CPU处理慢,设备需等待;当T > C时,设备传输慢,CPU需等待。两者不能完全并行。
4.3 双缓冲(Double Buffer)
基本思想
系统为设备分配两个缓冲区,实现并行操作。
工作方式
设备 → [缓冲区1] → 用户区 缓冲区2 ← 设备
设备 → [缓冲区2] → 用户区 缓冲区1 ← 设备
处理一块数据的时间 = max(C, T)
[!tip] 双缓冲的优势 当一个缓冲区的数据传送到用户区时,另一个缓冲区可同时接收设备的新数据,实现了设备与CPU的并行。
4.4 循环缓冲(Circular Buffer)
基本思想
系统分配多个大小相等的缓冲区,组成循环队列。
┌───┐ ┌───┐ ┌───┐ ┌───┐
输入 → │ B1 │ → │ B2 │ → │ B3 │ → │ B4 │ → 输出
└───┘ └───┘ └───┘ └───┘
↑ ↓
└────────────────────────┘
循环使用
- 输入指针(in):指向下一个可放入数据的缓冲区
- 输出指针(out):指向下一个可取出数据的缓冲区
适用场景:I/O速度与处理速度差异较大的系统。
4.5 缓冲池(Buffer Pool)
基本思想
系统从公用内存中分配一组缓冲区,组成缓冲池,供多个进程共享使用。
缓冲区队列
缓冲池中的缓冲区按用途分为三个队列:
| 队列 | 用途 |
|---|---|
| 空缓冲队列(emq) | 尚未使用的空缓冲区 |
| 输入队列(inq) | 装满输入数据的缓冲区 |
| 输出队列(outq) | 装满输出数据的缓冲区 |
四种工作缓冲区
| 缓冲区类型 | 功能 |
|---|---|
| 收容输入(hin) | 从空缓冲队列取缓冲区,接收输入数据,挂入输入队列 |
| 提取输入(sin) | 从输入队列取缓冲区,供CPU提取数据,释放回空缓冲队列 |
| 收容输出(hout) | 从空缓冲队列取缓冲区,接收CPU输出数据,挂入输出队列 |
| 提取输出(sout) | 从输出队列取缓冲区,供设备提取数据,释放回空缓冲队列 |
graph LR
subgraph 缓冲池
空缓冲队列["空缓冲队列 (emq)"]
输入队列["输入队列 (inq)"]
输出队列["输出队列 (outq)"]
end
输入设备 -->|"收容输入(hin)"| 空缓冲队列
空缓冲队列 -->|"收容输入(hin)"| 输入队列
输入队列 -->|"提取输入(sin)"| CPU
CPU -->|"提取输入(sin)"| 空缓冲队列
CPU -->|"收容输出(hout)"| 空缓冲队列
空缓冲队列 -->|"收容输出(hout)"| 输出队列
输出队列 -->|"提取输出(sout)"| 输出设备
输出设备 -->|"提取输出(sout)"| 空缓冲队列
style 空缓冲队列 fill:#e8f5e9
style 输入队列 fill:#e1f5fe
style 输出队列 fill:#fff3e0
五、设备分配与回收
5.1 设备分配中的数据结构
设备控制表(DCT)
每个设备一张,记录设备的状态和属性:
┌─────────────────────────────┐
│ 设备控制表 (DCT) │
├─────────────────────────────┤
│ 设备标识符 (设备ID) │
│ 设备类型 │
│ 设备状态:忙/空闲/故障 │
│ 指向控制器表的指针 (COCT) │
│ 重复执行次数/定时器 │
│ 设备队列的队首指针 │
└─────────────────────────────┘
控制器控制表(COCT)
每个设备控制器一张:
┌─────────────────────────────┐
│ 控制器控制表 (COCT) │
├─────────────────────────────┤
│ 控制器标识符 │
│ 控制器状态 │
│ 指向通道表的指针 (CHCT) │
│ 控制器队列的队首指针 │
└─────────────────────────────┘
通道控制表(CHCT)
每个通道一张:
┌─────────────────────────────┐
│ 通道控制表 (CHCT) │
├─────────────────────────────┤
│ 通道标识符 │
│ 通道状态 │
│ 通道连接的控制器列表 │
│ 通道队列的队首指针 │
└─────────────────────────────┘
系统设备表(SDT)
整个系统一张,记录所有设备的信息:
┌─────────────────────────────┐
│ 系统设备表 (SDT) │
├─────────────────────────────┤
│ 设备类 │
│ 设备标识符 │
│ DCT指针 │
│ 驱动程序入口地址 │
└─────────────────────────────┘
5.2 设备分配原则
| 考虑因素 | 说明 |
|---|---|
| 固有属性 | 独占设备、共享设备、虚拟设备 |
| 安全性 | 避免死锁(参考 12_死锁) |
| 效率 | 充分利用设备,提高系统吞吐量 |
5.3 独占设备、共享设备与SPOOLing
独占设备
同一时刻只能被一个进程使用(如打印机)。
共享设备
可被多个进程交替使用(如磁盘)。
SPOOLing技术(假脱机技术)
将独占设备改造为共享设备的技术,是虚拟设备的典型实现。
输入设备 ──→ 输入井 ──→ 内存 ──→ 输出井 ──→ 输出设备
(输入进程) (磁盘) (用户进程) (磁盘) (输出进程)
[!important] SPOOLing系统的核心思想 在磁盘上开辟输入井和输出井,用输入进程模拟脱机输入,用输出进程模拟脱机输出。用户进程不直接操作独占设备,而是与磁盘缓冲区交互,从而实现独占设备的共享。
经典应用:共享打印机——多个进程的打印请求先存入输出井,由输出进程依次输出到打印机。
5.4 设备分配流程
flowchart TD
A[进程请求设备] --> B{分配设备}
B -->|独占设备| C{设备空闲?}
C -->|是| D[分配设备, 修改DCT]
C -->|否| E[进程阻塞, 排入设备等待队列]
B -->|共享设备| F[直接分配]
D --> G{分配控制器}
F --> G
G --> H{分配通道}
H --> I[分配成功, 启动I/O]
I --> J[I/O完成]
J --> K[回收通道]
K --> L[回收控制器]
L --> M[回收设备]
六、磁盘调度算法
6.1 磁盘结构与性能参数
磁盘的物理结构
柱面 (Cylinder)
↓
┌───────────────────────┐
│ 磁头0 磁头1 磁头2 │ ← 磁头 (Head)
│ ───── ───── ───── │
│ 磁道0 磁道0 磁道0 │ ← 磁道 (Track)
│ 磁道1 磁道1 磁道1 │
│ ... ... ... │
│ 磁道n 磁道n 磁道n │
└───────────────────────┘
盘面 盘面 盘面
磁盘访问时间组成
总访问时间 T = 寻道时间 Ts + 旋转延迟 Tr + 传输时间 Tt
| 参数 | 含义 | 典型值 |
|---|---|---|
| 寻道时间 Ts | 磁头移动到目标磁道的时间 | 几ms ~ 几十ms(最主要) |
| 旋转延迟 Tr | 等待目标扇区旋转到磁头下的时间 | 转速7200rpm → 平均4.17ms |
| 传输时间 Tt | 数据读写时间 | 较短 |
[!important] 优化重点 寻道时间在总访问时间中占比最大,因此磁盘调度算法主要优化寻道时间。
6.2 常见磁盘调度算法
示例磁盘请求序列
假设当前磁头位置在磁道53,磁道范围0~199,请求队列:
请求队列: 98, 183, 37, 122, 14, 124, 65, 67
方向: 磁道号增加方向 (向磁道号大的方向移动)
FCFS(先来先服务)
原则:按请求到达的先后顺序依次服务。
磁头移动顺序: 53 → 98 → 183 → 37 → 122 → 14 → 124 → 65 → 67
移动距离:
|53-98| + |98-183| + |183-37| + |37-122| + |122-14| + |14-124| + |124-65| + |65-67|
= 45 + 85 + 146 + 85 + 108 + 110 + 59 + 2
= 640 磁道
平均寻道长度 = 640 / 8 = 80 磁道
[!note] FCFS特点
- 优点:公平、简单、不会产生饥饿
- 缺点:寻道距离大,性能差(磁头来回大幅移动)
- 适用:请求量少或对公平性要求高的场景
SSTF(最短寻道时间优先)
原则:选择距离当前磁头位置最近的请求优先服务。
当前位置: 53
请求队列: 98, 183, 37, 122, 14, 124, 65, 67
Step 1: 当前53, 最近是65(距离12)或37(距离16), 选65
Step 2: 当前65, 最近是67(距离2), 选67
Step 3: 当前67, 最近是37(距离30)或98(距离31), 选37
Step 4: 当前37, 最近是14(距离23), 选14
Step 5: 当前14, 最近是98(距离84), 选98
Step 6: 当前98, 最近是122(距离24), 选122
Step 7: 当前122, 最近是124(距离2), 选124
Step 8: 当前124, 最近是183(距离59), 选183
移动顺序: 53 → 65 → 67 → 37 → 14 → 98 → 122 → 124 → 183
移动距离:
|53-65| + |65-67| + |67-37| + |37-14| + |14-98| + |98-122| + |122-124| + |124-183|
= 12 + 2 + 30 + 23 + 84 + 24 + 2 + 59
= 236 磁道
平均寻道长度 = 236 / 8 = 29.5 磁道
[!warning] SSTF的"饥饿"问题
- 优点:平均寻道时间短
- 缺点:可能导致某些请求长期得不到服务(饥饿),尤其是距离较远的磁道请求
- 原因:距离远的请求总被距离近的新请求"插队"
SCAN(电梯算法/扫描算法)
原则:磁头沿一个方向移动,依次服务沿途的请求,到达磁道尽头后反向移动。
当前位置: 53, 方向: 磁道号增加
磁头移动: 53 → 65 → 67 → 98 → 122 → 124 → 183 → (到达尽头) → 37 → 14
移动距离:
(53→65): 12
(65→67): 2
(67→98): 31
(98→122): 24
(122→124): 2
(124→183): 59
(183→37): 146 ← 到达尽头后反向
(37→14): 23
总距离 = 12 + 2 + 31 + 24 + 2 + 59 + 146 + 23 = 299 磁道
平均寻道长度 = 299 / 8 = 37.4 磁道
[!note] SCAN特点
- 优点:寻道性能好,不会产生饥饿
- 缺点:两侧磁道的访问频率不均匀(中间磁道被更频繁访问)
- 类比:像电梯一样,先上后下(或先下后上)
C-SCAN(循环扫描算法)
原则:磁头单向移动,到达尽头后快速返回起始端(返回途中不服务),继续单向扫描。
当前位置: 53, 方向: 磁道号增加
磁头移动: 53 → 65 → 67 → 98 → 122 → 124 → 183 → (到达尽头)
→ 快速返回0 → 14 → 37
移动距离:
(53→65): 12
(65→67): 2
(67→98): 31
(98→122): 24
(122→124): 2
(124→183): 59
(183→0): 183 ← 快速返回
(0→14): 14
(14→37): 23
总距离 = 12 + 2 + 31 + 24 + 2 + 59 + 183 + 14 + 23 = 350 磁道
平均寻道长度 = 350 / 8 = 43.75 磁道
[!tip] C-SCAN vs SCAN
- C-SCAN 解决了 SCAN 中磁道访问频率不均匀的问题
- 返回过程不服务请求,使各磁道被访问的概率更均匀
- 总移动距离可能比SCAN略大
LOOK与C-LOOK
LOOK:SCAN的改进,磁头移动到最后一个请求就反向(不必到磁道尽头)。 C-LOOK:C-SCAN的改进,同理不必到磁道尽头。
当前位置: 53, 方向: 磁道号增加
LOOK: 53 → 65 → 67 → 98 → 122 → 124 → 183 → 37 → 14
(不必到达磁道199,到183就反向)
移动距离 = 12 + 2 + 31 + 24 + 2 + 59 + 146 + 23 = 299
注意: SCAN和LOOK在此例中结果相同,因为183已是最大请求
C-LOOK: 53 → 65 → 67 → 98 → 122 → 124 → 183 → 14 → 37
(不必返回磁道0,直接跳到最小请求14)
移动距离 = 12 + 2 + 31 + 24 + 2 + 59 + 169 + 23 = 322
6.3 磁盘调度算法对比
| 算法 | 寻道距离 | 公平性 | 饥饿问题 | 特点 |
|---|---|---|---|---|
| FCFS | 640 | 公平 | 无 | 简单,性能差 |
| SSTF | 236 | 不公平 | 有 | 性能好,可能饥饿 |
| SCAN | 299 | 较公平 | 无 | 性能好,两侧不均匀 |
| C-SCAN | 350 | 公平 | 无 | 各磁道均匀访问 |
| LOOK | 299 | 较公平 | 无 | SCAN的优化版本 |
| C-LOOK | 322 | 公平 | 无 | C-SCAN的优化版本 |
graph LR
subgraph 磁盘调度算法对比
A["FCFS<br/>距离: 640"] -->|"改进"| B["SSTF<br/>距离: 236"]
B -->|"解决饥饿"| C["SCAN<br/>距离: 299"]
C -->|"优化尽头"| D["LOOK<br/>距离: 299"]
C -->|"解决不均匀"| E["C-SCAN<br/>距离: 350"]
E -->|"优化尽头"| F["C-LOOK<br/>距离: 322"]
end
style A fill:#ffebee
style B fill:#fff3e0
style C fill:#e8f5e9
style D fill:#e1f5fe
style E fill:#f3e5f5
style F fill:#fce4ec
七、磁盘高速缓存
7.1 基本概念
磁盘高速缓存是在内存中为磁盘盘块设置的缓冲区,保存了某些盘块的副本。
工作原理:当有访问磁盘的请求时,先检查高速缓存中是否有所需盘块的数据。如果有(命中),直接从缓存读取;如果未命中,才启动磁盘读取,并将数据存入缓存。
请求访问盘块X
→ 查找高速缓存
→ 命中(Hit): 直接从缓存读取(速度提高几个数量级)
→ 未命中(Miss): 启动磁盘读取 → 数据送入缓存 → 返回给进程
7.2 数据交付方式
| 方式 | 说明 | 特点 |
|---|---|---|
| 数据交付 | 将缓存中的数据复制到进程的内存工作区 | 数据量大,有复制开销 |
| 指针交付 | 将指向缓存的指针交给进程 | 无复制,更高效 |
7.3 置换策略
| 算法 | 说明 |
|---|---|
| LRU(最近最久未使用) | 替换最长时间未被访问的盘块 |
| NRU(最近未使用) | 替换最近一个周期内未被访问的盘块 |
| LFU(最少使用) | 替换被访问次数最少的盘块 |
设计置换算法还需考虑:
- 访问频率:经常被访问的盘块应保留在缓存中
- 可预见性:某些盘块可能很快又被访问(如顺序读取时的预读)
- 数据一致性:确保缓存中的数据与磁盘中的数据一致
7.4 数据一致性保障
写回策略
- 立即写回:修改数据后立即写入磁盘(安全但性能低)
- 延迟写回:修改数据先保留在缓存中,稍后写回磁盘(性能高但有风险)
周期性写回
UNIX系统使用 sync 系统调用,周期性地将所有已修改的缓存数据强制写回磁盘,防止数据丢失。
UNIX update进程: 周期性调用 sync()
→ 将所有"脏"缓冲区的数据写回磁盘
→ 保障数据一致性
八、综合示例
8.1 磁盘调度算法计算练习
[!example] 练习题 某磁盘有200个磁道(0~199),当前磁头位于磁道100,向磁道号减小方向移动。请求队列为:
55, 58, 39, 18, 90, 160, 150, 38, 184。请分别用 FCFS、SSTF、SCAN、C-SCAN 算法计算总寻道距离。
FCFS:
100 → 55 → 58 → 39 → 18 → 90 → 160 → 150 → 38 → 184
= 45+3+19+21+72+70+10+112+146 = 498
SSTF:
100 → 90(10) → 58(32) → 55(3) → 39(16) → 38(1) → 18(20)
→ 150(132) → 160(10) → 184(24)
= 10+32+3+16+1+20+132+10+24 = 248
SCAN(向减小方向):
100 → 90 → 58 → 55 → 39 → 38 → 18 → (到达0) → 150 → 160 → 184
= 10+32+3+16+1+20+18+150+10+24 = 284
C-SCAN(向减小方向,单向):
100 → 90 → 58 → 55 → 39 → 38 → 18 → (到达0) → 快速到199
→ 184 → 160 → 150
= 10+32+3+16+1+20+18+199+15+24+10 = 348
九、I/O系统与其他子系统的关系
graph TB
A["用户程序"] -->|"系统调用"| B["I/O系统"]
B --> C["文件系统<br/>(参考 [[04_文件IO编程]])"]
B --> D["存储管理<br/>(参考 [[12_存储管理]])"]
B --> E["进程管理<br/>(参考 [[01_系统运行机制]])"]
C --> F["磁盘管理<br/>(参考 [[05_磁盘空间管理]])"]
D -->|"缓冲区分配"| B
E -->|"中断处理、进程调度"| B
F -->|"磁盘调度"| B
style A fill:#e1f5fe
style B fill:#fff3e0
style C fill:#e8f5e9
style D fill:#f3e5f5
style E fill:#fce4ec
style F fill:#ffebee
[!summary] 本章要点回顾
- I/O控制方式:程序直接控制 → 中断驱动 → DMA → 通道,CPU干预逐步减少
- I/O软件层次:中断处理程序 → 设备驱动 → 设备独立性软件 → 用户层软件
- 缓冲管理:单缓冲、双缓冲、循环缓冲、缓冲池,解决速度匹配问题
- 设备分配:考虑独占/共享/虚拟设备,SPOOLing将独占改造为共享
- 磁盘调度:FCFS、SSTF(最短寻道)、SCAN(电梯)、C-SCAN(循环扫描)
- 磁盘高速缓存:内存中缓存磁盘数据,配合LRU等置换算法
参考教材:汤小丹《计算机操作系统》第4版 第5章 下一篇:05_磁盘空间管理