938 lines
30 KiB
Markdown
938 lines
30 KiB
Markdown
# 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**
|
||
- 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)** | 从输出队列取缓冲区,供设备提取数据,释放回空缓冲队列 |
|
||
|
||
```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_磁盘空间管理]]
|