--- title: "08-IO管理:从轮询到通道" aliases: - IO管理 created: 2026-08-29 tags: - 基础与理论 - 操作系统 - "408" --- # IO管理:从轮询到通道 > 本篇对应王道第五章(原 17 IO管理概述 + 18 设备独立性软件合并)。主线一句话:**想尽办法减少 CPU 对 IO 的干预**——四种控制方式是这条主线的四步进化,缓冲区、SPOOLing 是配套工具。 ## 一、I/O 设备与控制器 IO 管理是 OS 设计中**最凌乱也最具挑战**的部分——设备五花八门,很难有通用一致的设计。 ### 设备分类(按不同维度) | 维度 | 分类 | | --- | --- | | **信息交换单位** | **块设备**(以块为单位,传输速率高、**可寻址**可随机读写,如磁盘)/ **字符设备**(以字符为单位,速率低、**不可寻址**,常用中断 IO,如键盘、打印机) | | **传输速率** | 低速(键盘鼠标,几~几百 B/s)/ 中速(激光打印机)/ 高速(磁盘、光盘) | | **使用特性** | 存储设备 / 输入输出设备(输入、输出、交互式) | | **共享属性** | **独占设备**(同一时刻只能一个进程用,如打印机)/ **共享设备**(时间段内多进程分时访问,如磁盘)/ **虚拟设备**(SPOOLing 把独占改造成共享) | ### 设备控制器:CPU 与设备之间的中间人 **I/O 接口(设备控制器)**接收 CPU 命令、控制设备工作,把 CPU 从繁杂的设备控制事务中解脱出来。三部分组成: ![[image-f3f78d85.png]] 1. **与 CPU 的接口**:数据线(数据/控制/状态信息)、地址线(寄存器编号)、控制线(读写信号) 2. **与设备的接口**:一个控制器可连多个设备,每接口传数据、控制、状态三类信号 3. **I/O 逻辑**:对 CPU 命令**译码**,控制所选设备 六大功能:接收识别命令、数据交换、报告设备状态、地址识别、数据缓冲、差错控制。 ### I/O 端口与编址 **I/O 端口** = 控制器中可被 CPU 直接访问的**寄存器**:数据寄存器(缓存输入输出数据)、状态寄存器(设备状态供 CPU 读)、控制寄存器(CPU 写入启动命令)。 编址两种方式: ![[image-293d18cd.png]] | | 独立编址 | 统一编址(内存映射 I/O) | | --- | --- | --- | | 思想 | IO 端口单独一个地址空间,用**专用 I/O 指令**访问 | 从主存地址空间分一段给端口,**普通访存指令**就能访问 | | 优点 | 地址线少、译码简单、寻址快;专用指令程序清晰 | 无需专门指令、灵活;编址空间大;保护可由虚拟存储机制实现 | | 缺点 | 指令少灵活性差;要两组读写控制信号 | **占用主存地址空间**;地址线全部参译码,电路复杂、速度慢 | ## 二、四种 I/O 控制方式(核心) 贯穿始终的宗旨:**尽量减少 CPU 对 I/O 控制的干预,把 CPU 从繁杂 IO 事务中解放出来**。 ### 1. 程序直接控制(轮询) CPU 发出 IO 指令后**不断循环测试设备状态**,直到数据就绪才取走。 ![[image-0ec1ba86.png]] 简单易实现,但 CPU 绝大部分时间耗在循环测试上,**CPU 与设备串行工作**——根源是没有中断机构,设备无法主动报告"我好了"。 ### 2. 中断驱动 设备准备好数据后**主动中断 CPU**:CPU 发出读命令后继续干别的;指令周期末尾检查中断信号,来了就保存现场、转中断处理程序、取走一个字,再恢复现场。 ![[image-c12529e4.png]] 设备准备数据期间 **CPU 和设备并行**,利用率明显提升。但仍有两个问题:数据交换**必须经过 CPU 寄存器**;CPU 以**字(节)为单位**干预——对块设备 IO 极其低效。 ### 3. DMA:数据不经过 CPU 在**设备和内存之间开辟直接数据通路**。三个特点: 1. 传送单位是**数据块**(不再是字) 2. 数据**直接在设备和内存间传送**,不经过 CPU 3. 仅在**一个/多个数据块的开始和结束**才需要 CPU 干预 ![[image-108228ef.png]] DMA 控制器四类寄存器:**CR**(命令/状态)、**MAR**(内存地址)、**DR**(数据)、**DC**(数据计数器)。工作流程:CPU 设置初值启动 DMA → DMA 控制器全权负责传送(每次一个字,不需 CPU)→ 整块传完发一个中断。 ### 4. 通道:让"小型处理机"管 IO **通道是一种特殊的处理机**,能执行一系列**通道指令**。CPU 只需发一条 IO 指令(指明通道程序在内存的位置和要访问的设备),通道自己执行通道程序完成任务后再中断 CPU。实现 **CPU、通道、设备三者并行**。 与一般处理机的区别:**指令类型单一、没有自己的内存**(通道程序放主机内存,与 CPU 共享)。与 DMA 的区别:DMA 需 CPU 控制传输块大小和内存位置,**通道这些信息自己控制**;一个 DMA 控制器对应一台设备,**一个通道可以控制多台设备**。 ### 裁缝店比喻(一图流记住四种方式) 想象客户要做一批衣服(CPU 要数据,裁缝是设备): | 方式 | 对应场景 | | --- | --- | | **程序轮询** | 裁缝没有客户联系方式,客户每隔一会儿**跑去裁缝店看**做好没有——浪费时间 | | **中断驱动** | 裁缝有联系方式,**每做完一件打一个电话**让客户来拿——省了瞎逛,但一件一趟还是累 | | **DMA** | 客户雇一位**专属秘书**,交代好衣服放哪(内存地址);裁缝直接联系秘书,秘书取回放好,**每 100 件报告一次**——大幅省时 | | **通道** | 秘书权力更大:**自己决定**衣服存哪、何时报告(100 件还是 10000 件);且**能同时对接多位裁缝**——CPU、通道、多设备全并行 | ## 三、I/O 软件层次结构 ![[image-65228dc9.png]] 从上到下四层,每层利用下层服务、屏蔽实现细节、向上提供服务;接口不变,改一层不影响别层;**只有最低层涉及硬件**: | 层次 | 职责 | | --- | --- | | **用户层软件** | 库函数(如 printf),通过系统调用获取 OS 服务 | | **设备独立性软件** | 统一接口、设备命名/保护、**分配与回收**、逻辑设备名→物理设备名映射、缓冲管理、差错控制、统一逻辑块 | | **设备驱动程序** | 与硬件直接相关:把抽象请求(read/write)转成设备命令发给控制器;每类设备一个驱动 | | **中断处理程序** | 保存被中断进程环境、处理后恢复现场;与硬件紧密相关,放最底层并尽量屏蔽 | **一次 read 的完整旅程**:用户调 read(用户层)→ 设备独立层解析统一命令 → 驱动层把 read 翻译成**该设备特有**的指令(磁盘和打印机行为不同)→ 中断处理程序接管执行 → 硬件控制器按命令操控设备完成。 ### 应用程序 I/O 接口 - **字符设备接口**:get/put(顺序存取,缓冲区收发字符)、in-control 通用指令(参数对应各设备特定功能)、open/close 实现互斥共享 - **块设备接口**:磁盘二维结构(磁道号+扇区号)被隐藏成**线性编号** 0~n-1;抽象命令映射为低层操作;常配 DMA;内存映射接口把文件映射到内存读写 - **网络设备接口**:**套接字接口**(socket),本地套接字连接远程套接字收发数据 ### 阻塞 I/O 与非阻塞 I/O - **阻塞 I/O**:进程发起 IO 后被移入阻塞队列干等,IO 完成才唤醒。**奶茶店点单守着等**——不知道啥时候做好,只能一直等。简单,适合并发小 - **非阻塞 I/O**:发起 IO 后不阻塞,但需**不断轮询**询问是否完成,等待期间能干别的。**点完单顺便逛商场,隔一会儿来问一次**。并发量大时合适,缺点是轮询占 CPU ## 四、高速缓存与缓冲区 ### 缓冲区要解决什么 1. 缓和 **CPU 与 IO 设备速度不匹配**的矛盾 2. **减少中断频率**,放宽中断响应时间限制 3. 解决**数据粒度不匹配**(设备按字、进程按块) 4. 提高 CPU 与设备的**并行性** 实现:硬件缓冲器太贵(只在关键部位用),一般用**内存**。共享资源 → 使用时**必须互斥**。 ### 单缓冲 一块缓冲区。设设备输入一块耗时 T、缓冲区传到工作区耗时 M、CPU 处理耗时 C,**缓冲区必须冲满才能取**: - T 与 C 可并行(设备往缓冲区灌数据时,CPU 处理上一块已传出的数据) - 平均处理一块时间 = **Max(C, T) + M** ### 双缓冲 两块轮流用:设备灌缓冲区 1 的同时,CPU 取缓冲区 2 的数据。T、M、C 三者并行程度更高: - 平均处理一块时间 = **Max(C + M, T)** ⚠️ 通信细节:两台机器间**单缓冲只能单向传输**;要双向就得**各自配两个缓冲区**(一个发送一个接收)。 ### 循环缓冲 速度相差太远时双缓冲也不够用——多个缓冲区链成**循环队列**,in 指针指向第一个可输入的空缓冲区,out 指向第一个可提取的满缓冲区,沿链循环移动。 ### 缓冲池 区别于缓冲区(只是一块内存):**缓冲池是管理机制**——数据结构 + 操作函数 + 公用缓冲区,**供多进程共享**。三类队列:空缓冲队列、输入队列(装满输入数据)、输出队列(装满输出数据);四种工作缓冲区(hin 收容输入 / sin 提取输入 / hout 收容输出 / sout 提取输出)。 ![[image-43f1da5e.png]] 四种工作方式:**收容输入**(空队首摘缓冲 → 装输入数据 → 挂输入队列尾)、**提取输入**(输入队列首取 → 提取数据 → 用完挂空队尾)、**收容输出**、**提取输出**——对称的四步循环。 > 💡 循环缓冲和缓冲池只需定性理解机理;**单缓冲双缓冲按 Max 公式模板分析,以不变应万变**。 ### 高速缓存 vs 缓冲区(必考对比) | | 高速缓存 Cache | 缓冲区 Buffer | | --- | --- | --- | | 存什么 | 数据的**副本** | 传输中的数据 | | 目的 | 访问副本比访问原始数据快 | 缓和速度差异、减少 IO 次数 | | 典型 | 磁盘高速缓存(**逻辑上属于磁盘、物理上驻留内存**的盘块信息)、TLB | IO 缓冲 | 磁盘高速缓存两种形式:内存中开辟**固定大小缓存区**;或把**未利用的内存空间**当缓冲池供请求分页和磁盘 IO 共享。 ## 五、设备分配与回收 总原则:**充分发挥设备使用效率(让设备忙起来)+ 避免分配不当造成死锁**。 ### 四张表(体现从属关系) 系统中通道 → 控制器 → 设备层层挂接,数据结构要体现这种层级: | 表 | 数量 | 内容 | | --- | --- | --- | | **DCT 设备控制表** | 每设备一张 | 设备类型、标识符(物理名唯一)、状态(忙/闲)、指向 COCT 的指针、重复执行次数或时间、**设备队列队首指针**(等待该设备的 PCB 队列) | | **COCT 控制器控制表** | 每控制器一张 | 指向 CHCT 的指针 | | **CHCT 通道控制表** | 每通道一张 | 与通道连接的控制器表首址(一个通道管多个控制器) | | **SDT 系统设备表** | **全系统一张** | 所有物理设备的表目(含 DCT 入口) | **回收**:进程释放设备且无其他进程请求 → 把 DCT 的设备状态改为空闲即可。 ### 分配三步 以独占设备为例:① 按**物理设备名**查 SDT 找到 DCT → 看状态:忙则挂设备等待队列,闲则分配设备;② 按 DCT 找 COCT → 忙则挂控制器等待队列,闲则分配控制器;③ 按 COCT 找 CHCT → 忙则挂通道等待队列,闲则分配通道。**设备、控制器、通道三者都分到,分配才算成功**,才能启动传送。 ### 分配要考虑的因素 - **固有属性**:独占设备(独占分配)、共享设备(同时分给多进程、调度访问次序)、虚拟设备(可共享) - **分配算法**:FCFS(按请求顺序排队)或最高优先级优先(同优先级再按 FCFS) - **安全性**:**安全分配**——发 IO 请求就阻塞直到完成,不会再申请别的资源,安全但 CPU 与设备**串行**;**不安全分配**——发完请求继续跑,可能同时操作多个设备、推进快,但**可能死锁** - **设备独立性**:进程用**逻辑设备名**请求,不指定具体物理设备——好处:分配灵活(该类设备忙可换同类另一台)、易于 **I/O 重定向**(换设备不用改程序) 逻辑设备名 → 物理设备名靠**逻辑设备表 LUT**(逻辑设备名、物理设备名、驱动程序入口地址)映射。LUT 两种设置:全系统一张(单用户系统,逻辑名不能重)或每用户一张(多用户系统,**不同用户可用相同逻辑名**)。 ![[image-c877a0bc.png]] ## 六、SPOOLing:把独占设备变成"共享" **假脱机技术**:用程序模拟脱机输入输出的外围控制机,将**独占设备改造成共享设备**。 组成: ![[image-fb049dd2.png]] - **输入井/输出井**:磁盘上开辟的两个存储区域,模拟脱机的磁盘;输入井收容 IO 设备输入的数据,输出井收容用户程序的输出数据;每进程的输入/输出数据是一个文件,链成队列 - **输入/输出缓冲区**:内存中暂存(设备 → 输入井、输出井 → 设备) - **输入/输出进程**:模拟外围控制机,搬运数据 - **井管理程序**:控制作业与磁盘井之间的信息交换 **打印机例子**:多个进程发打印请求,SPOOLing 系统都答应,但**不立即真把打印机分给谁**,而是:① 在磁盘缓冲区申请空闲盘块暂存打印数据;② 申请一张空白打印请求表挂到打印队列。进程立刻拿到"已打印"的感觉就去干别的了——真打印在打印机空闲且任务排到队首时才进行,用户不可见。每个进程都觉得自己**独占了一台打印机**。 **特点**: 1. 提高 IO 速度——对低速设备的操作变成对**磁盘缓冲区**的存取 2. **独占设备改造为共享设备**——实际并未给任何进程分配设备 3. 实现了**虚拟设备**功能 **为什么说是空间换时间**:空间开销显然(磁盘开井);省的时间在于——没有 SPOOLing 时 CPU 向打印机输出数据就得**迁就打印机的慢速度**,等打完才能干别的;有了 SPOOLing,CPU 把数据甩到磁盘输出井(快)就走人,打印排队的事假脱机进程管。向磁盘写数据比向打印机写快得多,时间就省出来了。 ## 七、设备驱动程序接口 驱动程序 = IO 系统上层与设备控制器之间的**通信程序**:接收抽象 IO 请求(read/write)转成具体命令发给控制器,再把控制器的信号传回上层。功能: 1. 把抽象要求转为具体要求(如盘块号 → 盘面号、磁道号、扇区号) 2. 检查请求合法性、了解设备状态、传递参数、设置工作方式 3. 发出 IO 命令:设备空闲立即启动;忙则把请求者 PCB 挂到设备队列 4. 及时响应控制器发来的中断,按类型调用相应中断处理程序 与普通程序的不同:抽象与具体的双向转换、与 IO 控制方式(中断/DMA)紧密相关、**不同类型设备配不同驱动**、基本部分常固化在 **ROM** 中、允许**同时多次调用执行**。 统一接口要求:每个驱动与 OS 之间有相同或相近的接口(好添加新驱动);抽象设备名转物理设备名找驱动入口;对设备保护,防止无权用户使用。 ## 八、盲点总复习 1. 四种 IO 方式的进化目标只有一句话:**减少 CPU 干预**——轮询全程盯 → 中断按字报 → DMA 按块报不经过 CPU → 通道自己跑程序管多设备 2. 通道**指令类型单一、没有自己的内存**(与 CPU 共享主机内存);一个通道可控制多台设备 3. 独立编址用专用 I/O 指令(要两组读写控制信号);统一编址占主存空间、译码复杂 4. 单缓冲 **Max(C,T)+M**,双缓冲 **Max(C+M,T)**;两机通信要双向必须各配**两个**缓冲区 5. 磁盘高速缓存**逻辑上属于磁盘、物理上在内存**——不是 CPU 与内存之间那个 Cache 6. 缓冲区是共享资源,使用必须**互斥**;缓冲池 = 缓冲区 + 管理机制,可多进程共享 7. 设备分配顺序:**设备 → 控制器 → 通道**,三者都到手才算成功 8. 安全分配方式(请求即阻塞)CPU 与设备串行;不安全分配方式推进快但可能死锁 9. 设备独立性 = 用**逻辑设备名**,好处是灵活 + IO 重定向;映射靠 **LUT**,多用户系统每用户一张 LUT(逻辑名可重) 10. SPOOLing 三大关键词:**磁盘上的井、内存中的缓冲区、模拟外围控制机的输入输出进程**;本质是**以空间换时间**实现虚拟设备 11. 驱动程序固化在 ROM;一个驱动对应**一类**设备 12. 字符设备不可寻址 → 顺序存取 + 中断方式;块设备可寻址 → 随机存取 + DMA --- 四大管理还剩最后一块拼图:磁盘调度 → [[09-磁盘与固态硬盘]] ⬅️ 🏠 [[00-操作系统总览]] ➡️ [[09-磁盘与固态硬盘]]