# 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软件
(库函数: printf, scanf)"] end subgraph 内核空间 B["设备独立性软件
(统一接口、缓冲、分配)"] C["设备驱动程序
(控制具体设备)"] D["中断处理程序
(响应设备中断)"] 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
距离: 640"] -->|"改进"| B["SSTF
距离: 236"] B -->|"解决饥饿"| C["SCAN
距离: 299"] C -->|"优化尽头"| D["LOOK
距离: 299"] C -->|"解决不均匀"| E["C-SCAN
距离: 350"] E -->|"优化尽头"| F["C-LOOK
距离: 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["文件系统
(参考 [[04_文件IO编程]])"] B --> D["存储管理
(参考 [[12_存储管理]])"] B --> E["进程管理
(参考 [[01_系统运行机制]])"] C --> F["磁盘管理
(参考 [[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_磁盘空间管理]]