设备管理
📖 阅读指南
知识依赖链:理解本章前,需要掌握:
- 进程管理(进程状态、调度、死锁)
- 内存管理(缓冲区概念)
- 中断机制(硬件中断的基本原理)
推荐阅读顺序:
1
2
3
4
5
4.1(硬件基础:设备 + 控制器 + I/O控制方式)
→ 4.2(软件层次:驱动 + 缓冲)
→ 4.3(独占设备分配策略)
→ 4.4(共享设备:磁盘调度 + RAID)
→ 4.5(虚拟设备:SPOOLing)
贯穿全章的核心矛盾:
1
2
3
CPU 速度 >> I/O 设备速度 → 缓冲 / DMA / 通道 / 并行
物理设备种类繁多 → 软件分层 / 设备无关性
独占 vs 共享 的冲突 → 分配策略 / SPOOLing 虚拟化
4.1 设备管理基础
4.1.1 设备分类与管理目标
为什么需要对设备分类?
操作系统对设备的管理策略取决于设备的特性。分类不是为了分类而分类,而是为了选择不同的调度、缓冲、分配机制。
三个分类视角
| 视角 | 类型 | 典型例子 | 管理意义 |
|---|---|---|---|
| 信息传输方向 | 输入 / 输出 / 双向 | 键盘 / 打印机 / 磁盘 | 决定数据流向 |
| 交互功能 | 人机 / 存储 / 机机通信 | 鼠标+显示器 / 磁盘 / 网卡 | 决定使用场景 |
| OS 管理视角(最重要) | 字符设备 / 块设备 / 网络设备 | 鼠标/串口 / 磁盘/CD / 网卡 | 决定 OS 调度策略 |
关键洞察:
- 块设备:可寻址(按地址读写任意块)、可随机访问、可缓存 → 磁盘调度、缓冲池
- 字符设备:按流顺序处理,不可寻址 → 字符队列、中断驱动
- 同一物理设备在不同场景下可能属于不同类别(如磁带在某些系统中可视为字符设备)
设备管理的核心目标
- 效率:让 CPU 与设备尽量并行工作,减少相互等待
- 抽象:屏蔽硬件细节,向上层提供统一接口(设备文件、裸设备)
- 公平:在多个进程竞争设备时,合理分配
设备管理功能清单:
- 中断处理
- 缓冲管理
- 设备分配与去配
- 驱动调度
- 虚拟设备(SPOOLing)
4.1.2 I/O 控制方式(演化史)
问题动机
CPU 与设备速度差异悬殊。如果 CPU 全程等待 I/O 完成,则大量时间浪费在等待上。
核心演化逻辑:逐步减少 CPU 介入 I/O 的程度。
1
2
3
4
5
6
7
┌─────────────────────────────────────────────────────────────────────┐
│ CPU 介入程度:全程参与 ───────────────────────────────→ 几乎不参与 │
│ │
│ 轮询方式 ──→ 中断方式 ──→ DMA方式 ──→ 通道方式 │
│ CPU忙等 CPU去做 CPU仅管 CPU下命令 │
│ 其他事 开始/结束 通道自主执行 │
└─────────────────────────────────────────────────────────────────────┘
设备控制器(Device Controller)
为什么需要设备控制器?CPU 不能直接操作物理设备(电压、时序等),需要一个中间层将高层命令翻译为设备的物理操作。
功能:
- 接收 CPU 命令(寄存器接口)
- 与设备进行数据交换
- 记录设备状态(状态寄存器)
- 地址识别(区分是发给自己的命令还是其他控制器的)
方式一:轮询(Polling / 忙等)
机制:CPU 发出 I/O 命令后,不断循环检查状态寄存器,直到设备就绪。
1
2
3
CPU: 发出命令 → 循环检查状态 → 读/写数据 → 继续下一个请求
↑_______________↓
(忙等循环)
问题:
- CPU 利用率极低(大量时间在空转等待)
- 适用于极简嵌入式系统(无 OS,资源极少)
方式二:中断驱动(Interrupt-Driven)
机制:CPU 发出 I/O 命令后继续执行其他任务;设备完成后向 CPU 发中断,CPU 响应并处理。
1
2
CPU: 发出命令 → 执行其他进程 → [中断到来] → 执行中断处理程序 → 继续
设备: ←————工作中————→ 发中断信号
优点:CPU 不再等待,可并行处理其他任务 局限:每传输一个字节/字就产生一次中断,中断频率极高,开销大 → 引出 DMA
方式三:DMA(Direct Memory Access,直接内存访问)
机制:CPU 只负责”启动”和”结束”,中间的数据搬运由 DMA 控制器自主完成(设备 ↔ 内存,不经过 CPU)。
1
2
CPU: 初始化DMA寄存器 → 执行其他任务 → [DMA完成中断] → 处理结果
DMA: ←—搬运数据(设备→内存 or 内存→设备)—→ 发中断
关键机制:周期窃取(Cycle Stealing)
为什么叫”窃取”?DMA 需要使用系统总线访问内存,而总线同一时刻只能被一个主设备使用。DMA 在 CPU 不需要总线的空隙”偷用”总线周期。
- DMA 每次”窃取” 1~几个主存周期
- 影响为何很小?CPU 大多数时间与 L1/L2 Cache 交互,极少直接访问主存,总线空闲时间充足
适用场景:大块连续数据传输(磁盘 I/O、网络数据包)
方式四:通道(I/O Channel)
机制:通道是一个专用的 I/O 处理器,CPU 在内存中组织”通道程序”,通道自主执行整个 I/O 序列,完成后中断 CPU。
四级层次结构:
1
2
3
4
5
6
7
处理器(CPU)
↓ 发起通道程序
通道(Channel)
↓ 发出控制命令
控制器(Controller)
↓ 物理驱动
设备(Device)
- 通道状态字(CSW):保存通道当前执行状态,用于 CPU 查询和错误恢复
- 适用场景:大型服务器、主机(IBM System/360 的经典设计)
四种方式对比
| 方式 | CPU忙等? | CPU参与数据搬运? | 中断频率 | 适用场景 |
|---|---|---|---|---|
| 轮询 | ✅ 全程 | ✅ 是 | 无中断 | 简单嵌入式 |
| 中断 | ❌ 否 | ✅ 是(每字节) | 极高 | 通用 OS |
| DMA | ❌ 否 | ❌ 否(块级别) | 低(块完成) | 大块 I/O |
| 通道 | ❌ 否 | ❌ 否(序列级) | 更低 | 大型服务器 |
4.1.3 总线结构与 I/O
问题根源
各设备数据速率相差数个数量级:
1
2
3
4
键盘 ~10 Bps
USB 鼠标 ~1 KBps
硬盘 ~200 MBps
内存总线 ~50 GBps
一条总线无法同时高效服务所有速率的设备。
总线结构演化
| 结构 | 核心思想 | 缺点 |
|---|---|---|
| 单总线 | 所有设备共享一条总线 | 慢速设备占用高速带宽,总线瓶颈严重 |
| 三级总线 | 处理器总线 + 内存总线 + I/O 总线分离 | 难以应对速率差异极大的设备 |
| 南桥/北桥 | 北桥连高速(CPU/内存/GPU),南桥连低速(USB/磁盘) | 北桥带宽仍是瓶颈;已被 PCH 取代 |
| 基于通道的服务器总线 | 多通道并发,每个通道独立总线 | 结构复杂,成本高 |
工程注记:现代 PC 中南北桥架构已被 Intel PCH(Platform Controller Hub)或 AMD Fusion Controller Hub 取代,实质是将北桥功能集成进 CPU Die,南桥功能集成进 PCH。
4.2 设备管理软件
4.2.1 I/O 软件层次结构
设计目标
- 高效率:充分并行,减少等待
- 通用性:不同设备对上层提供统一接口(”一切皆文件”的基础)
五层结构(自上而下)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌──────────────────────────────────────────────────────┐ ← 用户态
│ 5. 用户空间 I/O 软件 │
│ 库函数(printf/fread)、SPOOLing守护进程 │
├──────────────────────────────────────────────────────┤
│ 4. 独立于设备的 I/O 软件(Device-Independent Layer) │ ← 内核态
│ 命名、保护、缓冲、分配、错误报告 │
├──────────────────────────────────────────────────────┤
│ 3. 设备驱动程序(Device Driver) │
│ 将逻辑 I/O 请求翻译为物理操作序列 │
├──────────────────────────────────────────────────────┤
│ 2. I/O 中断处理程序(Interrupt Handler) │
│ 响应硬件中断、错误处理、唤醒等待进程 │
├──────────────────────────────────────────────────────┤ ← 硬件层
│ 1. I/O 硬件(控制器 + 设备) │
│ 执行实际物理操作 │
└──────────────────────────────────────────────────────┘
关键洞察:每层只与相邻层交互,层间通过标准接口通信。新增设备只需编写新驱动,上层软件无需修改。这是”设备无关性”的实现基础。
4.2.2 各层职责详解
层 2:中断处理程序
触发时机:设备完成操作或发生错误时
处理流程:
1
2
3
4
5
6
7
8
9
10
11
12
13
设备发中断
↓
保存 CPU 现场(寄存器)
↓
检查设备状态寄存器
↓
判断是否出错 ──→ 出错 → 错误恢复/报告
↓(正常)
唤醒等待该设备的进程(移入就绪队列)
↓
启动下一个排队的 I/O 请求
↓
恢复 CPU 现场,返回
层 3:设备驱动程序
职责:将操作系统的通用 I/O 请求(如”读第 N 块”)翻译为设备专属的控制序列(如”移臂到柱面 X、等待磁头 Y、读扇区 Z”)。
可分层实现:
- 类驱动(Class Driver):处理同类设备共性(如”SCSI 磁盘”的通用逻辑)
- 具体设备驱动:处理特定型号的差异(如 Samsung PM9A3 的特殊命令集)
工程现实:Linux 内核中驱动代码占总代码量约 70%,是内核最大的组成部分。
层 4:独立于设备的软件
负责跨设备的公共功能:
| 功能 | 含义 |
|---|---|
| 统一命名 | 设备映射为文件路径(/dev/sda) |
| 访问保护 | 权限控制(只有 root 可读写某些设备) |
| 缓冲管理 | 维护缓冲区池,协调速度差异 |
| 设备分配 | 独占设备的申请与释放 |
| 错误报告 | 将驱动层错误转换为标准错误码上报 |
层 5:用户空间 I/O 软件
- 库函数:
printf→write()系统调用(用户态缓冲,减少系统调用次数) - SPOOLing 守护进程:接管独占设备(详见 4.5)
4.2.3 I/O 缓冲机制
为什么需要缓冲?
三个动机:
- 速度不匹配:CPU 处理速度 » 设备 I/O 速度,缓冲暂存数据避免等待
- 减少中断次数:批量中断代替每字节中断,降低中断开销
- 提高并行性:CPU 处理缓冲中的数据同时,设备继续填充/清空另一个缓冲
工程本质:缓冲是”时间换时间”——用内存空间换取 CPU 与设备的时间解耦。
缓冲技术演化
单缓冲(Single Buffer)
1
2
3
时间线:
[设备写入 Buffer] → [CPU 处理 Buffer] → [设备写入 Buffer] → ...
CPU 和设备串行工作,无法并行
双缓冲(Double Buffer)
1
2
3
4
时间线:
[设备写 Buf1] [设备写 Buf2]
[CPU处理Buf1] [CPU处理Buf2]
CPU 和设备交替使用两个缓冲,实现并行
性能分析:
- 设备写入时间 T,CPU 处理时间 M,传输时间 C
- 单缓冲:每块耗时 = max(T, M) + C
- 双缓冲:每块耗时 = max(T, M+C)(当 T » M 时效果显著)
循环缓冲(Circular Buffer / Ring Buffer)
1
2
3
4
┌────┬────┬────┬────┬────┐
│ B0 │ B1 │ B2 │ B3 │ B4 │ (环形链表,in指针和out指针)
└────┴────┴────┴────┴────┘
↑出 ↑入
- 多个缓冲区组成环形链,设备持续填充,CPU 持续消费
- 适用于速度持续不匹配的场景(如网络数据包接收)
- Linux 内核的 pipe、sk_buff 均基于环形缓冲思想
| 缓冲类型 | 缓冲区数 | 并行性 | 适用场景 |
|---|---|---|---|
| 单缓冲 | 1 | 无 | 简单顺序 I/O |
| 双缓冲 | 2 | 读写可并行 | 一般 I/O |
| 循环缓冲 | N | 持续并行 | 流式数据、持续速率不匹配 |
4.3 独占型外围设备的分配
4.3.1 设备独立性
问题动机
若进程直接绑定物理设备(如”使用 /dev/lp0 号打印机”),则:
- 该设备故障 → 进程无法运行
- 同型号设备更换 → 程序需修改
- 设备繁忙 → 只能等待特定设备,无法使用同类空闲设备
解决:逻辑设备名机制
1
2
3
4
5
进程使用逻辑名 "PRINTER"
↓
OS 维护逻辑名 ↔ 物理名映射表
↓
实际映射到空闲的物理打印机(如 /dev/lp1)
优点:
- 程序与物理设备解耦,增删设备无需改代码
- 提高容错性(故障设备可透明切换)
- 增强分配灵活性(同类设备可负载均衡)
类比:这与 DNS 解析的思想一致——应用层使用域名,底层动态解析到 IP,切换服务器对上层透明。
4.3.2 独占型外设的分配策略
两种分配方式对比
| 方式 | 分配时机 | 持有周期 | 优点 | 缺点 |
|---|---|---|---|---|
| 静态分配 | 进程运行前(作业提交时) | 整个运行期 | 实现简单,绝不死锁 | 设备利用率低,设备长时间闲置 |
| 动态分配 | 运行中随用随申请 | 用完即释放 | 利用率高 | 可能产生死锁 |
动态分配的死锁风险
经典死锁场景:
1
2
3
4
进程 P:持有打印机 → 请求磁带机(等待 Q 释放)
进程 Q:持有磁带机 → 请求打印机(等待 P 释放)
↑
循环等待死锁
解决思路(连接第三章死锁知识):
- 按序申请资源(破坏循环等待条件)
- 一次性申请所有资源(类似静态分配,破坏持有并等待条件)
- 死锁检测与恢复
数据结构支撑
设备类表(Device Class Table):
| 设备类型 | 总台数 | 空闲台数 | 设备表指针 |
|---|---|---|---|
| 打印机 | 3 | 1 | → |
| 磁盘 | 5 | 3 | → |
设备表(Device Table):每台物理设备一行
| 物理名 | 逻辑名 | 占用进程 | 状态 | 等待队列 |
|---|---|---|---|---|
| /dev/lp0 | PRINTER | P1 | 忙 | P3, P4 |
| /dev/lp1 | - | - | 空闲 | - |
4.4 共享型外围设备的驱动(磁盘)
4.4.1 磁盘物理结构
层次结构
1
2
3
4
5
6
磁盘
├── 盘片(Platter)× N ← 双面磁性介质
│ ├── 磁道(Track) ← 同心圆,每面数千条
│ │ └── 扇区(Sector) ← 最小读写单元,通常 512B 或 4KB
│ └── 簇(Cluster) ← 相邻扇区的集合(文件系统分配单元)
└── 柱面(Cylinder) ← 不同盘面上半径相同的磁道集合
物理块地址:(柱面号 C,磁头号 H,扇区号 S)→ CHS 寻址(现已被 LBA 线性地址取代)
磁盘读写流程与时间构成
1
2
3
4
5
读写一块数据的时间:
寻道(Seek) 旋转等待 数据传输
[移臂到目标柱面] → [等待目标扇区转到磁头下] → [读/写数据]
Ts Tr(旋转延迟) Tt
存取时间公式:
1
2
3
4
5
6
Ta = Ts + Tr + Tt
其中:
Ts = 寻道时间(机械运动,通常 3~15ms)
Tr = 1/(2r) (平均旋转延迟 = 半圈时间,7200RPM 时约 4.2ms)
Tt = b/(rN) (传输时间,b=传送字节数,r=转速,N=磁道字节数)
数量级感知(7200 RPM HDD):
- 寻道:平均 ~8ms
- 旋转延迟:平均 ~4.2ms
- 传输时间:通常 < 1ms(相比前两者可忽略)
关键洞察:HDD 性能瓶颈在机械运动(寻道 + 旋转),而非数据传输本身。这是磁盘调度算法存在意义的根本原因,也是 SSD 彻底消除机械延迟后性能飞跃的原因。
4.4.2 磁盘驱动调度
移臂调度(减少寻道时间)
问题:多个进程并发请求不同磁道,若顺序服务则磁头来回跳动,寻道时间大。
算法一:FIFO(先来先服务)
策略:严格按请求到达顺序服务
示例(起点 53,队列:98, 183, 37, 122, 14, 124, 65, 67):
1
2
移动轨迹:53→98→183→37→122→14→124→65→67
移动总量:640 柱面
评价:公平,但磁头移动距离大,性能差。
算法二:SSTF(最短寻道时间优先)
策略:每次服务距当前磁头位置最近的请求
示例(同上):
1
2
53→65→67→37→14→98→122→124→183
移动总量:236 柱面
问题:饥饿(Starvation)——边缘磁道的请求可能永久等待(磁头总被中间请求”吸住”)
算法三:SCAN(扫描/电梯调度)
策略:磁头沿一个方向移动,服务途中所有请求;到达端点后换向。
1
2
3
4
5
6
7
← 方向示意 →
14 37 53 65 67 98 122 124 183
↑
起点(53),先向大方向扫描
移动:53→65→67→98→122→124→183→(换向)→37→14
移动总量:208 柱面
优点:兼顾公平与性能,端部也能被服务 缺点:端部磁道等待时间略长(需等磁头折返)
算法四:C-SCAN(循环扫描)
策略:只向一个方向扫描;到达端点后快速返回起点(返回途中不服务任何请求),再继续单向扫描。
1
53→65→67→98→122→124→183→(返回0)→14→37
优点:各磁道等待时间更均匀(消除端部不公平)
算法五:N-step-SCAN
策略:将请求队列分成长度为 N 的批次,每次只扫描当前批次;新来的请求加入下一批。
解决的问题:磁头粘性(Arm Stickiness)——SSTF/SCAN 中若请求持续出现在磁头附近,磁头会”粘”在该区域,远端请求饥饿。
算法六:FSCAN(两队列扫描)
策略:维护两个队列 F 和 N;扫描时只服务 F 中的请求,新来请求加入 N;F 服务完毕后 F/N 交换。
优点:防止磁头粘性,同时保证吞吐量。
算法性能对比
| 算法 | 示例移动量(起点53) | 公平性 | 饥饿风险 | 适用场景 |
|---|---|---|---|---|
| FIFO | 640 | 高 | 无 | 请求少或均匀分布 |
| SSTF | 236 | 低 | 有 | 实时性要求高,请求集中 |
| SCAN | 208 | 中 | 无 | 通用服务器 |
| C-SCAN | ~200 | 高 | 无 | 请求均匀分布、公平性优先 |
| N-step-SCAN | 中等 | 高 | 无 | 高并发写场景 |
旋转调度(减少旋转延迟)
问题:同一柱面有多个请求时,服务顺序影响旋转等待时间。
循环排序:将同一柱面的多个扇区请求,按磁盘旋转方向排成最优服务顺序,避免”刚过去又等一圈”。
优化扇区分布(交替编址):
1
2
3
4
5
朴素编号:扇区 1,2,3,4,5,6...(连续编号)
问题:读完扇区1处理数据时,扇区2已转过去,需等几乎一整圈
交替编号:1,3,5,2,4,6...(间隔编号,匹配 CPU 处理时间)
效果:读完扇区1处理完毕时,扇区2恰好转到磁头下
工程数字:优化扇区分布可将连续读取时间从 214ms 降至 70ms(同一数据量)。
4.4.3 RAID 磁盘冗余阵列
背景与动机
为什么需要 RAID?
1
2
3
4
5
CPU 年性能提升:~30-50%
磁盘年性能提升:~7%
→ 性能差距持续扩大
→ 需要通过磁盘并行(条带化)和冗余(容错)弥补
RAID 的两个目标:
- 性能:多盘并行读写(条带化 Striping)
- 可靠性:冗余保护(镜像 Mirroring 或 奇偶校验 Parity)
各 RAID 级别详解
| RAID 级别 | 冗余方式 | 读性能 | 写性能 | 空间利用率 | 容错能力 | 典型用途 |
|---|---|---|---|---|---|---|
| RAID 0 | 无(纯条带) | N 倍 | N 倍 | 100% | ❌ 零容错 | 视频编辑、临时数据 |
| RAID 1 | 镜像(全拷贝) | N 倍(多头读) | 最慢盘速度 | 1/N | 坏 N-1 块仍可用 | 系统盘、关键数据 |
| RAID 2 | 海明码(逐位) | <N 倍 | 受限 | n/(n+k) | 可纠 1 位错 | 已淘汰(ECC 内存替代) |
| RAID 3 | 字节级奇偶(1块专用校验盘) | N-1 倍 | 受限(校验盘瓶颈) | (N-1)/N | 坏 1 块 | 流媒体顺序读写 |
| RAID 4 | 块级奇偶(1块专用校验盘) | N-1 倍 | 受限(校验盘瓶颈) | (N-1)/N | 坏 1 块 | 基本已被 RAID5 取代 |
| RAID 5 | 块级分布奇偶(校验分散到所有盘) | N 倍 | 略低于 RAID0 | (N-1)/N | 坏 1 块 | 最常用,通用存储 |
| RAID 6 | 双重奇偶(P+Q,两种独立校验) | N 倍 | 略低于 RAID5 | (N-2)/N | 坏 2 块 | 企业级存储,高可靠 |
| RAID 10 | RAID 1+0(先镜像再条带) | N 倍 | N/2 倍 | 1/2 | 坏 N-1 块(每对镜像各坏1) | 高性能 + 高可靠 |
为什么 RAID 4 被淘汰?专用校验盘成为写操作瓶颈——每次写数据盘都必须更新校验盘,校验盘的 I/O 次数 = 所有数据盘 I/O 之和。RAID 5 将校验块分散到所有盘,消除单点瓶颈。
RAID 5 写惩罚:每次写需要”读旧数据 + 读旧校验 + 计算新校验 + 写新数据 + 写新校验”(4 次 I/O),写性能显著低于 RAID 0。这是 RAID 5 的核心权衡。
4.4.4 磁盘 Cache 与 SSD 补充
磁盘 Cache
机制:在主存中为磁盘扇区建立缓冲区,利用局部性原理(时间/空间局部性)预取和缓存热点扇区。
替换策略:
- LRU(最久未访问替换):缓存最近使用的,替换最久未访问的
- LFU(最少使用频率替换):替换历史访问次数最少的
HDD vs SSD
| 指标 | HDD(7200RPM) | SSD(NVMe PCIe 4.0) |
|---|---|---|
| 顺序读速度 | ~200 MB/s | ~7300 MB/s |
| 随机读延迟 | ~8ms | ~0.1ms |
| 机械运动 | 有(寻道+旋转) | 无 |
| 需要移臂调度 | ✅ 必要 | ❌ 无意义 |
| 寿命 | 不受写次数影响 | 有写寿命(P/E Cycles) |
| 适用场景 | 大容量冷数据 | 系统盘、热数据 |
关键洞察:SSD 消除了机械延迟,使磁盘调度算法(移臂调度)失去意义——NVMe SSD 的随机 I/O 性能与顺序 I/O 接近,操作系统对 SSD 使用简化的调度器(如 Linux 的 none 或 mq-deadline)。
4.5 虚拟设备与 SPOOLing
4.5.1 SPOOLing 系统
问题动机
场景:打印机是独占设备,速度极慢。
若进程直接独占打印机:
- 打印 10MB 文件需要几分钟,期间打印机被独占
- 其他进程必须等待,系统吞吐量极低
- 打印机大部分时间空闲(进程准备数据时打印机闲置)
核心思想
用共享型高速磁盘模拟独占型慢速设备,让进程感觉”自己独占了打印机”,实际上只是在向磁盘写文件。
SPOOLing = Simultaneous Peripheral Operations On-Line(假脱机技术)
机制详解
1
2
3
4
5
6
7
8
9
10
11
12
13
14
┌────────────────────────────────────────────────────────────────┐
│ SPOOLing 系统架构 │
│ │
│ 输入设备 输出设备(打印机) │
│ │ ↑ │
│ ↓ │ │
│ [预输入程序] [缓输出程序] │
│ │ │ │
│ ↓ │ │
│ 磁盘输入井 ←──[井管理程序]──→ 磁盘输出井 │
│ ↕ │
│ 运行中的进程 │
│ 进程从输入井读数据,向输出井写数据(看似直接 I/O) │
└────────────────────────────────────────────────────────────────┘
三个软件组件:
| 组件 | 职责 |
|---|---|
| 预输入程序(Spooler In) | 将输入设备的数据预先读入磁盘输入井,进程需要时直接从磁盘取(高速) |
| 缓输出程序(Spooler Out) | 将磁盘输出井中的数据缓慢输出到实际设备(在后台进行) |
| 井管理程序(Well Manager) | 管理进程与输入/输出井之间的数据交换(本质是 I/O 重定向) |
效果与优势
- 设备共享化:多个进程可同时”使用”打印机(实际都在写磁盘),打印机逻辑上变为共享设备
- 缩短进程内存驻留时间:进程输出的数据快速写入磁盘(高速),进程可以立即释放内存继续或退出
- 提高系统吞吐量:打印机始终处于忙碌状态(守护进程源源不断地从输出井取数据打印)
打印 SPOOLing 的实际工作方式
1
2
3
4
5
6
7
8
9
用户进程 打印守护进程(lpd/CUPS) 打印机
│ │ │
│ 生成完整打印文件 │ │
│ → 存入打印目录 │ │
│ → 通知守护进程 │ │
│(进程完成,可继续做其他事) │ │
│ 守护进程检测新文件 │
│ → 按顺序取文件 │
│ → 发送到打印机 ──────────────→ │
工程实现:Linux 中的 CUPS(Common Unix Printing System)即是 SPOOLing 思想的现代实现;Windows 的 Print Spooler 服务同理。
4.5.2 批处理系统的作业管理
作业的四个状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌──────────────┐
│ 输入状态 │ ← 输入设备的数据正在被预输入
│ (预输入中) │
└──────┬───────┘
│ SPOOLing 预输入完成
↓
┌──────────────┐
│ 后备状态 │ ← 等待作业调度器选中
│ (就绪待调度) │
└──────┬───────┘
│ 作业调度(资源满足)
↓
┌──────────────┐
│ 运行状态 │ ← 进程正在执行
│ (进程运行中) │
└──────┬───────┘
│ 作业运行结束
↓
┌──────────────┐
│ 完成状态 │ ← 输出结果正在被缓输出
│ (缓输出中) │
└──────┬───────┘
│ SPOOLing 输出完成
↓
结束
作业调度算法
| 算法 | 策略 | 适用场景 |
|---|---|---|
| 优先数调度 | 按预设优先级选作业 | 有明确优先级需求 |
| 短作业优先(SJF) | 预计运行时间最短的先调度 | 提高吞吐量,降低平均等待时间 |
| 响应比调度 | (等待时间+运行时间)/运行时间 最高者先 | 兼顾短作业和长等待作业 |
| 设备搭配 | 综合考虑设备需求,避免死锁 | I/O 密集型混合作业 |
作业调度 vs 进程调度
| 维度 | 作业调度(长程调度) | 进程调度(短程调度) |
|---|---|---|
| 操作对象 | 从后备队列选作业 | 从就绪队列选进程 |
| 执行结果 | 创建进程(从磁盘调入内存) | 分配 CPU |
| 调度频率 | 低频(分钟级) | 高频(毫秒级) |
| 决策维度 | 资源是否足够(内存、设备) | CPU 时间分配策略 |
附录
A. 常见误区对照表
| 误区 | 正确理解 |
|---|---|
| DMA 完全不需要 CPU | DMA 需要 CPU 初始化参数(基地址、长度、方向),完成后还需 CPU 响应中断 |
| RAID 1 读性能等于单盘 | RAID 1 可同时从多个镜像盘并行读取,读性能可达 N 倍 |
| RAID 5 只有写性能问题 | RAID 5 在盘故障时读性能也下降(需实时重建数据) |
| SSD 不需要任何调度 | SSD 仍需 I/O 调度(公平性、优先级),只是不需要移臂调度 |
| SPOOLing 让进程真正共享设备 | SPOOLing 是”虚拟共享”——物理设备仍由守护进程独占,进程操作的是磁盘上的虚拟映射 |
| 缓冲越多越好 | 缓冲过多消耗内存,且数据在缓冲中停留时间越长,崩溃丢失的数据越多(写缓冲的持久性问题) |
| SSTF 总体性能最好 | SSTF 寻道时间最短,但可能导致饥饿,整体系统公平性差 |
| 磁盘 Cache 替换一定用 LRU | 顺序扫描场景下 LRU 会导致”缓存污染”(一次性读取的大量数据挤出真正热点数据),需用 LFU 或特殊策略 |
B. 关键概念速查表
| 概念 | 一句话定义 |
|---|---|
| 设备控制器 | CPU 与 I/O 设备之间的硬件中间层,翻译 CPU 命令为设备物理操作 |
| 周期窃取 | DMA 在 CPU 不使用内存总线的间隙借用总线访问内存的机制 |
| 通道程序 | CPU 预先在内存中组织的 I/O 操作序列,供通道自主执行 |
| 设备独立性 | 进程通过逻辑名访问设备,OS 负责逻辑名到物理名的映射 |
| 磁头粘性 | SSTF 调度中磁头因局部请求密集而长期停留在某区域的现象 |
| 条带化 | 将数据分块分散存储到多个磁盘,实现并行读写(RAID 0 的基础) |
| SPOOLing | 用高速共享磁盘虚拟化慢速独占设备的技术 |
| 输入/输出井 | SPOOLing 系统中磁盘上用于缓存 I/O 数据的临时区域 |
| 作业调度 | 从后备队列选择作业调入内存、创建进程的低频调度(长程调度) |
| 进程调度 | 从就绪队列选择进程分配 CPU 的高频调度(短程调度) |
C. 本章知识依赖图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
第三章(死锁)
└──────────────→ 4.3 独占设备分配(动态分配的死锁风险)
第二章(进程/中断)
├──────────────→ 4.1.2 中断驱动方式
├──────────────→ 4.2.2 中断处理程序
└──────────────→ 4.5.2 作业调度 vs 进程调度
内存管理(缓冲区概念)
└──────────────→ 4.2.3 I/O 缓冲机制
本章内部依赖:
4.1(硬件基础)
├──→ 4.2(软件层次,基于 4.1 控制方式)
│ └──→ 4.3(分配策略,基于 4.2 设备独立性)
└──→ 4.4(磁盘调度,基于 4.1 磁盘物理结构)
└──→ 4.5(SPOOLing,基于 4.4 磁盘作为高速介质)