I/O 接口的基本结构
上一节说清了接口要做什么,这一节说它长什么样。
看接口结构图只需抓住一件事:这块电路有两张脸。 左边那张脸对着系统总线,说的是标准协议;右边那张脸对着设备,说的是设备方言。中间的寄存器是两张脸唯一的交接点——CPU 只能碰到寄存器,设备也只能碰到寄存器,两者从不直接见面。
机制
一张图看全
flowchart LR subgraph HOST["← 主机侧(面向 CPU)"] AD["地址线"] DT["数据线"] CT["控制线<br/>读/写/中断请求"] end subgraph IFACE["I/O 接口"] DEC["地址译码器<br/>设备选择"] DBR["数据缓冲寄存器<br/>(数据端口)"] STA["状态寄存器<br/>(状态端口)"] CTL["控制寄存器<br/>(控制端口)"] LOGIC["控制逻辑"] INTR["中断请求触发器 INTR"] MASK["中断屏蔽触发器 MASK"] end subgraph DEV["设备侧 →"] SIG["设备专用信号<br/>启动/就绪/数据"] end AD --> DEC DT <--> DBR DT --> CTL DT --> STA CT --> LOGIC DEC --> LOGIC LOGIC --> DBR LOGIC --> STA LOGIC --> CTL STA --> INTR INTR --> MASK MASK -->|"INT 请求线"| CT DBR <--> SIG CTL --> SIG SIG --> STA classDef host fill:#e3f2fd,stroke:#1565c0 classDef reg fill:#c8e6c9,stroke:#1b5e20,stroke-width:2px classDef int fill:#ffcdd2,stroke:#b71c1c,stroke-width:2px classDef dev fill:#fff3e0,stroke:#e65100 class AD,DT,CT host class DBR,STA,CTL,DEC,LOGIC reg class INTR,MASK int class SIG dev
图上有三处值得停一停。
第一,数据线是双向的,但只对数据缓冲寄存器双向。 状态寄存器只读(CPU 读它,设备写它),控制寄存器只写(CPU 写它)。方向不对称,这一点在编程和做题时都会用到。
第二,控制字和状态字都走数据线。 不要因为名字里有”控制”就以为它走控制线——控制线上传的是”这次是读还是写”这种总线级信号,控制寄存器里存的是”启动打印机”这种设备级命令。两者层次完全不同。这是本节最容易混的一对。
第三,右下角那两个触发器(INTR / MASK)是整个 7.3.2 的物理基础。 它们不是可有可无的附件,见下文。
三类寄存器
| 寄存器 | 别名 | CPU 的操作 | 内容 |
|---|---|---|---|
| 数据缓冲寄存器 DBR | 数据端口 | 读或写 | 一次传送的数据 |
| 状态寄存器 | 状态端口 | 只读 | 忙/就绪/出错/完成 |
| 控制寄存器 | 控制端口 | 只写 | 命令字:启动、方向、中断允许 |
状态位里最重要的是两个,它们决定了整个查询和中断流程:
| 触发器 | 名字 | 含义 |
|---|---|---|
| 工作(忙)触发器 | 设备正在工作,不能打扰 | |
| 完成(就绪)触发器 | 设备已完成本次操作,数据可取 |
| 含义 | ||
|---|---|---|
| 0 | 0 | 空闲,可以接受新命令 |
| 1 | 0 | 正在工作,CPU 要等 |
| 0 | 1 | 工作完成,数据准备好了 |
| 1 | 1 | 不出现(矛盾状态) |
程序查询方式的循环,查的就是
中断请求触发器与屏蔽触发器:5.5.3 的屏蔽字长在这里
这两个触发器解释了一件之前悬着的事。
5.5.3 讲过中断屏蔽字:每个中断源对应屏蔽字里的一位,写 1 表示屏蔽。当时说的是”屏蔽字是软件可写的寄存器”,但没说这一位在硬件上落在哪里。
答案就是这里:屏蔽字的每一位,物理上就是某个接口里的中断屏蔽触发器 MASK。
信号的通路是串联的:
读法:设备好了才置 INTR,没被屏蔽才发得出去。
疑问点:中断被屏蔽之后,那个中断请求是不是就丢了
没丢。这正是 INTR 和 MASK 分成两个触发器的理由。
如果屏蔽是直接把请求”掐掉”,那么用一个触发器就够了。分成两个,是为了让”请求已经产生”和”请求能不能发出去”分开保存:
- INTR 记录”设备确实提过请求”——它由设备置位,不受屏蔽影响。
- MASK 决定”这个请求现在能不能传到 CPU”——它由软件写入。
所以屏蔽期间,INTR 一直是 1,请求处于挂起(pending)状态;一旦软件把 MASK 清 0,请求立刻传到 CPU,中断随即被响应。
这与 5.5.3 那句”中断屏蔽不是中断请求消失”是同一件事的硬件解释。 也正因为请求会挂起,屏蔽字改变的是”处理优先级”而不是”响应优先级”——被屏蔽的中断源迟早还是会被处理,只是排到了后面。
一个推论:如果某个中断源被长期屏蔽而它又不断产生请求,INTR 只是一位,多次请求会合并成一次——这就是”中断丢失”在真实硬件里的样子,与”屏蔽导致丢失”是两回事。408 不考这一层,但知道它有助于理解为什么中断服务程序里要及时清 INTR。
接口在总线上的两副身份
普通接口是从设备(6.1.1 的判据:谁发地址谁是主)。CPU 发出端口地址,接口应答,接口全程不发地址。
但接口能主动发出的信号有两条,这两条不违反上面的判据:
| 信号 | 性质 | 为什么不算主设备 |
|---|---|---|
| 中断请求 INT | 一根独立的请求线 | 它不携带地址,只是”请求注意” |
| DMA 请求 DREQ | 一根独立的请求线 | 同上;只有获准之后,DMA 控制器才开始发地址,那时才成为主设备 |
“接口能主动发信号”和”接口是从设备”不矛盾——发请求不等于控制总线。判据始终是”这次总线事务的地址由谁给出”。
边界
| 说法 | 判断 | 理由 |
|---|---|---|
| ”控制字通过控制线传送” | ❌ | 通过数据线传到控制寄存器;控制线传的是读/写等总线信号 |
| ”状态寄存器 CPU 可读可写” | ❌ | 一般只读(个别位有写 1 清零的副作用,题目会说明) |
| ” | ❌ | 这是矛盾状态,不会出现 |
| ”被屏蔽的中断请求会被丢弃” | ❌ | 挂起在 INTR 里,解除屏蔽后仍响应 |
| ”接口能发中断请求,所以它是主设备” | ❌ | 判据是谁发地址,请求线不携带地址 |
| ”一个接口只有一个寄存器” | ❌ | 至少三类,见 7.2.4 |
| ”中断屏蔽触发器在 CPU 里” | ⚠️ | 屏蔽字的位分布在各接口里;CPU 里的是总中断允许位 IF/EI,两者是不同的层级 |
对照速查
| 触发器 | 位置 | 谁置位 | 谁清零 |
|---|---|---|---|
| 接口状态寄存器 | 启动命令 | 设备完成时 | |
| 接口状态寄存器 | 设备完成时 | CPU 取走数据后 | |
| INTR 中断请求 | 接口 | 中断被响应/处理后 | |
| MASK 中断屏蔽 | 接口 | 软件写屏蔽字 | 软件写屏蔽字 |
| IF / EI 总中断允许 | CPU | 开中断指令 | 关中断指令、中断隐指令 |
四道关卡,任何一道不通,中断都发生不了。
考点
- 控制字走数据线,不走控制线——极高频。
、 两个触发器的状态组合。- INTR 与 MASK 分离的意义:请求挂起不丢失。
- 屏蔽字的物理载体在接口里,总中断允许位在 CPU 里。
- 接口是从设备,中断/DMA 请求线不改变这个身份。
链接
- 🏠 返回总览:计算机组成原理第 7 章:输入/输出系统总览
- ⬅️ 上一节:7.2.1 I/O 接口的功能
- ➡️ 下一节:7.2.3 I/O 接口的类型
- 🔗 屏蔽字与嵌套的完整机制:5.5.3 异常和中断响应过程
- 🔗 主/从设备判据:6.1.1 总线的基本概念
- ➡️ 这两个触发器怎么用:7.3.2 程序中断方式
- 📖 名词库:第 7 章名词库