I/O 系统
选择题高频考点,主要两点:1. IO 接口和端口。2. 几种 IO 方式的细节和区别。
此外,在解答题中偶尔也会结合总线指标考查一些计算过程,所以需要深入了解几种 IO 方式的细节。
输入/输出系统
以主机为中心 来看,将信息从外部设备传送到 主机 的过程称为 输入;将信息从 主机 传送到外部设备的过程称为 输出。
简单架构
让我们首先来看一个早期简易 I/O 系统的架构(一般考查的也就是这种架构):
在这个架构中,各部分的基本功能如下:
- CPU 是整个计算机系统的控制和运算核心,它负责:发出控制命令(如读/写指令)以驱动外设;通过 I/O 总线 访问设备控制器,进行地址寻址、数据传输和状态监测;与 内存 协同,完成数据的读取、处理与存储。
- 外设 是计算机与外部世界交互的工具,例如键盘、打印机和磁盘等。它们接收来自 CPU 的输出(如打印任务),也能将物理信号(如按键动作)转换为数字信号传递给 CPU。
- I/O 接口 是连接 主机 与 外设 之间信息传输的桥梁,负责协调两者之间的同步、速度匹配和数据格式转换等问题。它在结构上包含两个部分:
- 主机侧接口:作为 内部接口,通过系统总线与 CPU 和 主存(内存)连接;
- CPU ↔ 设备控制器:通过系统内部总线(如 PCI、PCIe)进行连接
- 外设侧接口:作为 外部接口,通过 USB、串口、并口等电缆与具体 外设 连接。
- 设备控制器 ↔ 外设:通过各种外部接口标准(如 USB、SATA、HDMI、VGA 等)进行连接
- 主机侧接口:作为 内部接口,通过系统总线与 CPU 和 主存(内存)连接;
通过 I/O 接口,系统在 CPU、内存 和 外设 之间建立了一条高效的信息传输通路。这个通路大致如下:
I/O 接口 和 设备控制器 的关系
设备控制器更强调控制逻辑与功能;I/O 接口更强调连接位置与通信角色。两者其实并不是严格等同,但在很多场景下它们是同一个物理实体的不同视角。可以将 I/O 接口视为设备控制器的一部分。
现代架构
现代 I/O 硬件系统的架构更加复杂,这节不会直接考查,了解即可:
在现代系统中,CPU 本身集成了部分高速 I/O 控制逻辑(如 内存控制器、PCIe 控制器、DMI 接口等),而非核心的 I/O 控制被下放至 芯片组(PCH),也就是过去所谓的“南桥”。这将 CPU 从大量外围管理工作中解放出来,提升性能并降低功耗。
- CPU 内部
- 包含 内存控制器 和部分 PCIe 控制器
- 与 PCH 之间通过高速互连(如 DMI / Direct Media Interface)通信
- PCH(Platform Controller Hub)
- 管理几乎所有非高速设备(如 USB、SATA、音频、网卡等)
- 是现代主板上的中心管理芯片,连接大量外设控制器
硬件和软件层
上文提到的是 I/O 系统的 硬件层次,除了硬件部分,I/O 系统还包括一系列的 软件组件,用以管理和调度外设资源、提供抽象接口、增强设备的可用性和通用性。主要的 软件层次 如下:
- 用户层 I/O 软件
- 设备独立性软件
- 设备驱动程序
- 中断处理程序
软硬件协同使得 I/O 操作既可高效运行,又具有高度的可移植性和扩展性。操作系统通过抽象和分层,使得新的设备只需添加驱动程序即可兼容原有的 I/O 系统,而不需要修改上层软件。
I/O 接口
结构
- 数据缓冲:CPU 寄存器、主存 等主机部件的存取速度比外设快很多,因此为了速度匹配,可将数据提前存到 I/O 接口 中的数据缓冲区,用以中和高速系统组件和低速外部设备之间的速度差异
- 数据格式转换:主机和外设的数据格式不同,I/O 接口 提供数据格式的相互转换,如串并转换、数模转换等
- 控制和定时:为来自系统总线的控制信号和定时信号提供控制和定时逻辑,对内部资源及外设间动作的先后关系进行协调,并对数据通信过程进行控制
- 错误或状态检测:提供可以保存各种状态信息的状态寄存器,CPU 通过读取这些寄存器来了解接口和外设的出错情况或状态
- 与主机和设备通信:上述四个功能都必须通过主机与 I/O 接口之间和 I/O 接口 与 外设 之间的通信来完成
I/O 端口
I/O 端口(Input/Output Port)是指 I/O接口电路中,CPU可直接访问的寄存器。CPU通过向这些端口发送命令、读取状态或交换数据,来实现对外部设备的间接控制。I/O端口是连接主机与外设的 软件可编程窗口,是操作系统和设备驱动程序进行I/O操作的核心对象。
基本分类
根据寄存器所承担的功能不同,I/O端口通常分为以下三类:
- 数据端口:用于存放CPU与外设之间待传输的实际数据,可读可写。
- 状态端口:用于存放外设或接口自身的状态信息(如“忙/闲”“就绪/未就绪”),通常为只读。
- 控制端口:用于接收CPU发出的控制命令(如“启动”“停止”“复位”),通常为只写。
编址方式
为了方便 CPU 对于 I/O 端口的访问,需要对各个端口进行编号,每个端口对应一个端口地址。
编址方式主要分为 统一编址 和 独立编址 两种。
统一编址也叫做 内存映射 I/O(Memory-Mapped I/O,MMIO),具备如下特点:
- I/O 设备的寄存器 直接映射到内存地址空间,CPU 将设备寄存器当作普通内存来访问。
- CPU 使用普通的内存访问指令(如
MOV、LDR、STR等)即可完成读写,不需要专门的 I/O 指令。 - 优点是 统一寻址、编程方便、指令通用性强,但会占用一部分主存地址空间。
例如,假设某设备的控制寄存器映射到了地址 0xF0001000,CPU 可以像访问普通变量一样:
MOV EAX, [0xF0001000] ; 读取设备寄存器
MOV [0xF0001000], EBX ; 向设备寄存器写数据
实际上访问的是设备寄存器,而不是 RAM。
现代计算机(如 ARM、RISC-V、以及 x86 中的大多数 PCIe 设备)几乎都采用 MMIO。
独立编址独立编址也叫做 端口映射 I/O(Port-Mapped I/O,PMIO),具备如下特点:
- I/O 地址空间与内存地址空间 完全分离,CPU 维护一套独立的 I/O 地址。
- CPU 不能使用普通内存读写指令,而必须使用专门的
IN、OUT指令访问 I/O 端口。 - 每个设备被分配一组 固定的端口号,例如传统 PC 中:
- 键盘控制器:
0x60、0x64 - 串口 COM1:
0x3F8 - 并口 LPT1:
0x378
- 键盘控制器:
例如,在 x86 中读取键盘控制器的数据:
IN AL, 0x60 ; 从端口 0x60 读取一个字节
向串口发送一个字节:
OUT 0x3F8, AL ; 将 AL 中的数据写入串口
由于 IN/OUT 指令只能访问 I/O 端口,因此普通的内存地址不能使用这两条指令进行访问。
下表给出了 MMIO 和 PMIO 这两种方式的对比:
| 编址方式 | 别名 | 地址空间 | 访问方式 | 是否占用主存地址空间 | 是否使用 IN/OUT |
|---|---|---|---|---|---|
| 统一编址 | 内存映射 I/O(MMIO) | 与内存共享 | 普通内存读写指令(如 MOV) | ✅ 是 | ❌ 否 |
| 独立编址 | 端口映射 I/O(PMIO) | 独立 I/O 地址空间 | 专用 IN/OUT 指令 | ❌ 否 | ✅ 是 |
对于考研来说,核心掌握两点即可:
- MMIO:设备寄存器映射到内存地址空间,使用普通内存读写指令访问。
- PMIO:设备拥有独立的 I/O 地址空间,只能使用
IN/OUT等专用 I/O 指令访问。
现代处理器(如 ARM、RISC-V)几乎只支持 MMIO;而传统 x86 为了兼容早期 PC,至今仍同时支持 MMIO 和 PMIO。
I/O 方式
I/O 设备的速度通常远低于 CPU,如果 CPU 一直等待设备完成操作,将会造成大量时间浪费。
因此,操作系统和硬件提供了多种 I/O 方式,核心目的都是:
- 协调 CPU 与 I/O 设备之间的速度差异;
- 尽可能减少 CPU 等待 I/O 的时间,提高系统整体效率。
根据 CPU 在数据传输过程中参与程度 的不同,常见的 I/O 方式可分为四种:
| I/O 方式 | CPU 是否负责数据搬运 | CPU 是否需要等待设备 | 特点 |
|---|---|---|---|
| 程序查询方式 | ✅ | ✅ 不断轮询) | 实现最简单,但 CPU 利用率最低 |
| 程序中断方式 | ✅ | ❌ | 设备完成后主动通知 CPU |
| DMA 方式 | ❌(DMA 控制器负责) | ❌ | CPU 只负责初始化,效率高 |
| 通道方式 | ❌(通道负责) | ❌ | 大型计算机使用,可管理多个设备 |
下面分别介绍这四种 I/O 方式。
程序查询方式
程序查询方式不断轮询设备状态以判断其是否可以进行数据传输,数据从 设备 → CPU → 主存。
程序查询方式包含以下步骤:
- 查询:CPU 定期查询或检查 I/O 设备的状态寄存器,查看设备是否准备就绪。
- 判断:如果设备未就绪,CPU 会继续执行其他任务或持续轮询;如果设备就绪,则进行数据传输。
- 数据传输:CPU 直接与 I/O 设备交换数据,可以是读操作或写操作。
- 结束:数据传输完成后,CPU 继续执行其他任务,同时继续轮询 I/O 设备状态。
程序中断方式
在程序中断方式中,当 I/O 设备 准备好 进行数据传输时,会触发一个 中断 来通知 CPU。CPU 会暂停当前正在执行的任务,处理中断,然后恢复执行被中断的任务。
相比于轮询方式,CPU 在执行程序中断之前不需要一直检查 I/O 设备的状态,这提升了效率。
DMA 方式
DMA(Direct Memory Access,直接内存访问)是一种计算机系统的 I/O(输入/输出)方式,它允许 外部设备直接与主内存交换数据,而无需 CPU 在数据传输过程中逐次参与。
有了上面的 程序中断方式,虽然在 一定程度上 实现了 CPU 和外设的并行工作,但这种并行并不彻底。CPU 只是在外设准备数据期间可以继续执行其他程序,而当外设完成一次数据准备后,仍需响应中断,进入中断服务程序,将数据从 I/O 接口寄存器 搬运到 主存。
由于一次中断通常只能完成少量数据的传输,因此面对高速 I/O 设备或大量数据传输时,CPU 需要频繁地暂停现行程序,不断进入中断服务程序完成数据搬运。这不仅增加了中断处理的开销,也使 CPU 花费大量时间充当 数据搬运者,影响了系统的整体性能。
DMA 方式正是为了解决程序中断方式的这一缺陷而提出的。它通过增加 DMA 控制器(DMA Controller),使数据能够直接在 I/O 接口 和 主存 之间传输,CPU 只需在传输开始前完成参数配置,在整个数据传输过程中无需参与具体的数据搬运,仅在传输结束后接收一次完成中断即可。因此,DMA 不仅实现了 数据传输过程与主程序真正并行工作,还大幅减少了 CPU 的数据搬运开销和中断次数,从而显著提高了高速、大批量数据传输的效率。
原理
DMA 让数据传送直接在 外设和内存之间 开辟一条 数据通路,使得数据传送期间不再需要占用 CPU 时间处理中断、保护断点和恢复 CPU 现场等繁琐操作。
在 DMA 方式中,CPU 只参与预处理(CPU 通过初始化程序来设置 DMA 控制器的各参数寄存器的初始值,如内存首地址、字计数值等)和后处理过程(传送完成后 CPU 会进行数据校验等工作)。通常适用于以块读写的设备。
这意味着在 DMA 方式下,CPU 的核心计算资源几乎不受数据传输的影响。
通过 DMA 方式,外设和内存之间进行数据传输是否还需要经过 CPU?
外设写入内存时,数据“绕过了 CPU 的寄存器和执行单元”,但仍然必须“经过 CPU 芯片内部的互连与 内存控制器”来完成写入,整个过程由 CPU 授权和调度,不占用 CPU 核心计算资源。
比如我们通过下表对该过程进行 更加精细化的分解 🔍 :
| 层次 | 是否经过 | 说明 |
|---|---|---|
| CPU 的 寄存器 / 执行单元 | ❌ 否 | DMA 不用 CPU 寄存器搬数据,也不打断指令流 |
| CPU 的 互连结构(SoC Fabric) | ✅ 是 | 所有 I/O 访问在 CPU 芯片内都要通过互连 |
| CPU 的 内存控制器(IMC) | ✅ 是 | 最终访问内存必须经过内存控制器,现代 CPU 内部集成 |
| CPU 的 授权 / 控制路径 | ✅ 是 | DMA 之前需配置地址、长度、权限等控制结构 |
DMA 控制器
DMA 控制器是实现 DMA 传输的专用硬件,其主要负责接收 CPU 的配置、申请系统总线控制权,并在 I/O 接口 与 主存 之间自动完成数据传输。其主要组成如下:
- 主机 - 控制器接口
- DR(数据寄存器):暂存一次总线传输的数据。
- MAR(内存地址寄存器):保存当前主存地址,每完成一次数据传输后自动更新。
- DC(数据计数器):记录剩余待传输的数据量,每完成一次数据传输后自动递减。
- CR(命令/状态寄存器):保存 CPU 配置的 DMA 工作参数(如传输方向、工作方式等),同时记录 DMA 的工作状态。
- I/O 控制逻辑
- 块设备 - 控制器接口
教材中常说 DMA 以"块"为单位进行传输,这里的"块"并不是指 DMA 在一次总线周期内就传输完整块数据。实际上,DMA 底层仍然按照总线传输单位(如字节、字、Cache Line 等)逐次完成数据交换,每完成一次传输都会自动更新 MAR 和 DC。所谓"块传输",是指 CPU 只需配置一次 DMA 控制器,DMA 便能够自动、连续地完成整块数据的传输,而无需 CPU 在每次数据搬运时参与。
举个例子,对于 MAR = 0x100000,DC = 4096,那么 DMA 的逻辑就类似于以下代码描述的流程:
while (DC > 0) {
搬一个总线传输单位;
MAR += unit;
DC -= unit;
}
当 DC 减到 0 时,DMA 传输结束,通过一个中断告诉 CPU 传输完成。
DMA 控制器和 CPU 的关系
上图展示的是早期计算机系统中的一种典型实现。在传统的单总线结构中,DMA 控制器(DMAC) 是一个独立于 CPU 的硬件模块,通过系统总线连接主存和 I/O 控制器。当需要进行 DMA 传输时,DMA 控制器首先向 CPU 申请系统总线的控制权,随后接管总线,在 I/O 接口 与 主存 之间直接完成数据传输。
现代计算机中,DMA 功能通常已经集成到 CPU、芯片组(PCH)、I/O 控制器,甚至 PCIe 网卡、SSD、GPU 等高速设备内部,因此现代系统更多是集成的 DMA 引擎(DMA Engine),而不是独立的 DMA 芯片。不过,它们的工作原理与早期 DMA 控制器基本一致。
步骤
DMA 方式与中断 I/O 方式都采用“请求—响应”的工作机制,但二者请求的对象不同:
- 中断 I/O 方式请求 CPU 暂停当前程序,进入中断服务程序处理数据传输。
- DMA 方式主要请求系统总线的控制权,由 DMA 控制器代替 CPU 完成外设与主存之间的数据传输。
如下图所示,为进程 P1 通过 DMA 在磁盘和主存之间进行数据交换的过程:
一次完整的 DMA 传输通常可以分为 预处理、数据传输和后处理 三个阶段。
预处理
CPU 首先对 DMA 控制器进行初始化,设置本次传输所需的参数,主要包括:
- 外设地址或 I/O 接口地址;
- 主存起始地址;
- 数据传输长度;
- 数据传输方向;
- DMA 的工作方式。
设置完成后,CPU 启动外设和 DMA 控制器,随后便可以继续执行其他程序。
DMA 请求
当外设准备好数据,或者具备接收数据的条件后,外设向 DMA 控制器发出 DMA 请求。
DMA 控制器接收到请求后,向 CPU 或总线仲裁器申请获得系统总线的控制权。
取得总线控制权
CPU 在完成当前总线周期后,向 DMA 控制器发出总线响应信号,并暂时让出总线控制权。
DMA 控制器取得总线控制权后,开始执行 DMA 传输周期。
数据传输
DMA 控制器直接在 I/O 接口和主存 之间传输数据,不需要 CPU 逐次参与数据搬运。
每传输一个数据单位,DMA 控制器通常会自动:
- 修改主存地址;
- 更新传输计数;
- 判断本次传输是否已经完成。
在 DMA 传输期间,CPU 仍可继续执行不依赖该总线的操作;当 CPU 与 DMA 控制器同时需要使用总线时,需要通过总线仲裁决定使用权。
释放总线控制权
当规定数量的数据全部传输完成后,DMA 控制器结束 DMA 操作,并释放系统总线,使总线控制权重新交还给 CPU。
后处理
DMA 传输完成后,DMA 控制器通常向 CPU 发出一次 完成中断。
CPU 响应中断后进入相应的中断服务程序,完成后续处理,例如:
- 检查 DMA 传输是否成功;
- 读取并清除 DMA 状态;
- 更新进程或 I/O 请求的状态;
- 唤醒等待数据传输完成的进程;
- 启动下一次 I/O 操作。
因此,在 DMA 方式下,CPU 主要负责传输前的初始化和传输后的处理,而大批量数据的实际搬运由 DMA 控制器完成。
使用总线方式
在 DMA 方式中,DMA 接口和 CPU 共享主存,因此它们同时访问主存时,会出现总线的争用问题,为了实现主存的分时复用,有以下三种方法:
- 总线独占
(Bus Mastering):
- 在这种模式下,DMA 控制器会取得对总线的 独占控制权。在传输期间,CPU 将无法访问内存,因为总线已经被 DMA 控制器占用了。
- 在独占期间,CPU 通常会执行不涉及内存访问的指令,如计算或寄存器操作,或者进入等待状态直到 DMA 操作完成。
- 周期挪用
(Cycle Stealing,也叫做循环窃取):
- 在循环窃取模式下,DMA 控制器会 逐个窃取总线周期 来进行数据传输,而不是一次性占据所有的总线周期。
- 这意味着 CPU 在 DMA 控制器未使用总线时仍然可以访问内存。因此,DMA 和 CPU 会交替使用总线,通常不会显著影响 CPU 的操作。
- 分时多路复用
(Time-Division Multiplexing):
- 在分时多路复用模式下,DMA 控制器和 CPU 会在 预定的时钟周期内轮流使用总线。
- 这种方式确保了 CPU 和 DMA 控制器都可以在它们的时隙内访问内存,但都无法全时段访问。
CPU 和 DMA 控制器哪一个对于 总线访问的优先级更高?
当 CPU 和 DMA 都需要访问主存时,DMA 控制器可以优先获得总线使用权:
因为磁盘一旦开始读写,就必须按时完成数据传送,否则数据缓冲区中的数据会发生丢失。
通道方式
通道方式(Channel I/O)是在 DMA 的基础上进一步发展而来的 I/O 方式,主要用于大型机和早期高性能计算机。
DMA 方式虽然能够实现 设备与主存之间直接传输数据,但 每完成一次 DMA 传输,CPU 都需要重新启动下一次 DMA 操作。当系统同时管理大量高速 I/O 设备时,CPU 仍需要频繁参与各个 DMA 的初始化工作。
为了解决这一问题,引入了 通道(Channel)。
通道本质上是一种 专门负责 I/O 的处理器(又称 I/O Processor),能够独立执行一系列 I/O 指令,因此也称为 通道处理机。
CPU 只需向通道下达一次 I/O 命令,之后整个 I/O 过程都由通道负责完成,无需 CPU 再参与具体的数据传输和设备控制。
因此,数据传输路径变为:设备 ↔︎ 通道 ↔︎ 主存
CPU 与通道之间仅交换控制信息,而数据在设备和主存之间直接传输。
工作过程
通道方式通常包含以下几个步骤:
- CPU 初始化:CPU 将通道程序(Channel Program)的首地址告诉通道,并启动通道工作。
- 通道执行:通道依次执行通道程序中的各条 I/O 指令,自动控制设备完成数据传输。
- 数据传输:数据直接在 设备 ↔ 主存 之间传输,不经过 CPU。
- 结束中断:整个 I/O 操作完成后,通道向 CPU 发出中断,通知任务结束。
整个过程中,CPU 可以一直执行其他程序,只需在开始和结束时参与。
通道程序
通道程序(Channel Program)是一组存放在主存中的 通道指令。
这些指令规定了:
- 操作哪个设备;
- 数据存放的位置;
- 数据传输长度;
- 下一条通道指令的位置等。
CPU 不负责执行这些指令,而是由 通道 独立解释和执行。
通道与 DMA 的区别
| 比较项 | DMA | 通道 |
|---|---|---|
| 控制者 | DMA 控制器 | 通道(I/O 处理机) |
| 是否具有执行指令能力 | ❌ 没有 | ✅ 有,可执行通道程序 |
| CPU 参与程度 | 每次 DMA 都需 CPU 初始化 | CPU 只需启动一次通道程序 |
| 能否管理多个设备 | 一般较弱 | ✅ 可以同时管理多个设备 |
| 应用场景 | 微机、PC、服务器 | 大型机、高性能计算机 |
四种 I/O 方式可以理解为 CPU 参与程度逐渐降低的过程:
- 程序查询:CPU 负责等待 + 数据搬运。
- 程序中断:CPU 不等待,但仍负责数据搬运。
- DMA:CPU 不等待,也不负责数据搬运,只负责初始化 DMA。
- 通道:CPU 连 DMA 的启动细节都交给通道,只负责下达 I/O 任务。