Files

938 lines
30 KiB
Markdown
Raw Permalink Normal View History

2026-06-13 23:46:22 +08:00
# 17 I/O系统
> **课程**:操作系统
> **关联章节**[[01_系统运行机制]] | [[04_文件IO编程]] | [[05_磁盘空间管理]]
---
## 一、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与设备之间的接口
> **设备控制器的功能**
> 1. 接收和识别CPU发来的命令
> 2. 实现CPU与控制器、控制器与设备之间的数据交换
> 3. 记录设备的状态供CPU查询
> 4. 识别设备地址(端口地址)
---
## 二、I/O控制方式
I/O控制方式的发展目标**减少CPU对I/O过程的干预**提高CPU与I/O设备的**并行操作**程度。
### 2.1 程序直接控制方式Programmed I/O
#### 基本思想
CPU通过不断**轮询polling**设备状态寄存器直接控制I/O操作的全过程。
#### 工作流程
```mermaid
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。
#### 工作流程
```mermaid
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发来的命令和状态
└─────────────────────────────────┘
```
#### 工作流程
```mermaid
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指令通道就能**自主完成**整个数据传输过程。
#### 通道的类型
| 类型 | 特征 | 传输单位 |
|------|------|----------|
| **字节多路通道** | 以字节为单位交叉传输多个设备 | 字节 |
| **数组选择通道** | 一次只服务一个设备,传输速率高 | 数据块 |
| **数组多路通道** | 上述两种的结合,兼顾效率和利用率 | 数据块 |
#### 工作流程
```mermaid
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软件采用**分层设计**思想,每一层利用下层提供的服务,为上层提供更高级的服务。
```mermaid
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完成的进程
- 恢复现场,返回被中断的进程
```c
// 中断处理程序伪代码
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 引入缓冲的原因
1. **缓和CPU与I/O设备速度不匹配**的矛盾
2. **减少对CPU的中断频率**,放宽对中断响应时间的限制
3. **提高CPU与I/O设备之间的并行性**
### 4.2 单缓冲Single Buffer
#### 基本思想
系统为设备分配**一个**缓冲区。
#### 工作方式
处理一块数据的时间 = **max(C, T) + M**
- CCPU处理一块数据的时间
- 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** | 从输出队列取缓冲区,供设备提取数据,释放回空缓冲队列 |
```mermaid
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 设备分配流程
```mermaid
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的优化版本 |
```mermaid
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系统与其他子系统的关系
```mermaid
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] 本章要点回顾
> 1. **I/O控制方式**:程序直接控制 → 中断驱动 → DMA → 通道CPU干预逐步减少
> 2. **I/O软件层次**:中断处理程序 → 设备驱动 → 设备独立性软件 → 用户层软件
> 3. **缓冲管理**:单缓冲、双缓冲、循环缓冲、缓冲池,解决速度匹配问题
> 4. **设备分配**:考虑独占/共享/虚拟设备SPOOLing将独占改造为共享
> 5. **磁盘调度**FCFS、SSTF最短寻道、SCAN电梯、C-SCAN循环扫描
> 6. **磁盘高速缓存**内存中缓存磁盘数据配合LRU等置换算法
---
> **参考教材**汤小丹《计算机操作系统》第4版 第5章
> **下一篇**[[05_磁盘空间管理]]