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数据端口读或写一次传送的数据
状态寄存器状态端口只读忙/就绪/出错/完成
控制寄存器控制端口只写命令字:启动、方向、中断允许

状态位里最重要的是两个,它们决定了整个查询和中断流程:

触发器名字含义
工作(忙)触发器设备正在工作,不能打扰
完成(就绪)触发器设备已完成本次操作,数据可取

和 的组合有明确的语义,而且它们不是简单的互补:

含义
00空闲,可以接受新命令
10正在工作,CPU 要等
01工作完成,数据准备好了
11不出现(矛盾状态)

程序查询方式的循环,查的就是 ;中断方式的中断请求,源头也是 的置位。

中断请求触发器与屏蔽触发器: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 请求线不改变这个身份。

链接