3. 中断响应过程:CPU如何响应中断
好,咱们接着聊中断。前面讲了中断从哪来、怎么分类,现在该聊聊最核心的问题了——CPU到底是怎么响应中断的?
我个人习惯把中断响应过程拆成四个阶段:采样 → 识别 → 响应 → 处理。你想想看,CPU又不是长了眼睛,它怎么知道外设喊它了?这里面有一套非常精密的硬件机制。
3.1 中断采样:CPU怎么知道有中断来了?
CPU在执行指令的时候,并不是一直盯着中断引脚看。那样太累了,也不现实。实际上,CPU是在每个指令周期的最后一个时钟周期,才去采样中断请求信号。
嗯,这里要注意:采样不是随时随地的。CPU有自己的节奏——它只在固定的时间窗口去看一眼中断引脚。这个时间点,就是指令周期的末尾。
中断采样时机:
- 每个指令周期的最后一个时钟周期
- CPU检查INTR(可屏蔽中断)或NMI(不可屏蔽中断)引脚
- 如果检测到有效电平/边沿,则进入中断响应流程
我在项目中遇到过一个问题:有个同事写了一个死循环指令,里面全是NOP(空操作),结果中断死活不响应。为什么?因为那个NOP指令本身就是一个完整的指令周期,CPU在每个周期末尾都会采样,按理说应该能响应啊。后来查了半天才发现——他把中断屏蔽位给置1了。说白了,采样到了也没用,CPU根本不搭理。
3.2 中断响应周期:CPU的“特殊总线周期”
一旦CPU采样到有效的中断请求,并且中断是允许的(IF=1),CPU就会进入一个特殊的总线周期——中断响应周期。
这个周期和普通的指令周期不一样。我习惯把它理解成:CPU对外设说“我收到你的请求了,现在告诉我你是谁,你要干什么”。
中断响应周期通常包含两个总线周期(以x86为例):
| 阶段 | 操作 | 说明 |
|---|---|---|
| 第一个总线周期 | CPU发出INTA#信号(低电平有效) | 通知中断控制器:我已准备好响应 |
| 第二个总线周期 | CPU再次发出INTA#信号 | 中断控制器将中断类型号送上数据总线 |
你可能会问:为什么要两个周期?第一个周期是让中断控制器有时间做优先级裁决,第二个周期才是真正读取中断向量号。这是硬件设计上的一个巧妙安排。
避坑指南:我曾经在一个ARM Cortex-M3的项目里,忽略了中断响应周期的时序要求,结果中断控制器返回的中断号总是错的。后来发现是CPU的INTA信号和中断控制器的数据准备时间没对齐。记住:硬件时序不是你想当然就能对的,一定要看数据手册。
3.3 中断识别:CPU怎么知道是哪个设备?
采样到了,也响应了,接下来最关键的一步:识别中断源。CPU怎么知道是键盘按了、鼠标动了、还是网卡来数据了?
主要有三种方式:
- 向量中断:中断控制器直接告诉CPU中断号,CPU查表找到入口地址。这是现代CPU最常用的方式。
- 查询中断:CPU挨个问每个设备“是不是你?”效率低,但简单。我在早期的8位机上见过。
- 菊花链中断:设备串成一串,谁先响应谁就获得服务。优先级由物理位置决定。
我个人最喜欢向量中断的方式——干净利落。中断控制器在第二个INTA周期把中断类型号(比如0x20~0x2F)放到数据总线上,CPU拿到这个号,直接去中断向量表里找对应的处理函数地址。整个过程只需要几个时钟周期。
中断向量表(IVT)示例:
; 假设中断类型号为 0x21(键盘中断)
; CPU 执行:
; 1. 类型号 × 4 = 0x84(每个表项4字节)
; 2. 从内存 0x0000:0x0084 处读取4字节
; 3. 低2字节是偏移地址,高2字节是段地址
; 4. 跳转到该地址执行中断处理程序
说白了,中断识别就是一次“查表跳转”。CPU不需要知道具体是哪个设备,它只需要知道去哪个地址执行代码。真正区分设备的逻辑,是在中断处理程序里完成的。
3.4 中断响应过程的完整流程
把上面这些串起来,一个完整的中断响应过程是这样的:
- 采样阶段:CPU在指令周期末尾检查中断引脚
- 判断阶段:检查IF标志位,判断是否允许中断
- 保护现场:自动压栈PSW(标志寄存器)和PC(返回地址)
- 响应周期:CPU发出INTA信号,读取中断类型号
- 查表跳转:根据中断类型号从中断向量表找到入口地址
- 执行处理:跳转到中断处理程序执行
你想想看,从外设发出中断请求,到CPU开始执行处理程序,中间就这几个步骤。硬件设计得越精巧,这个过程就越快。
重要提醒:中断响应过程中,CPU会自动关闭中断(IF=0),防止在处理当前中断时又被其他中断打断。如果你需要支持中断嵌套,必须在处理程序里手动开中断。我见过有人忘记开中断,结果高优先级的中断永远得不到响应——系统直接“假死”。
3.5 一张图看懂中断响应
下面我用SVG画了一张流程图,把整个中断响应过程串起来。你看完应该能有个整体印象。
这张图把整个流程串起来了。你从“CPU执行指令”开始,顺着箭头往下走,每一步都清清楚楚。我个人建议你把这张图记在脑子里——面试的时候画出来,面试官会觉得你基本功很扎实。
3.6 几个容易踩的坑
最后,我分享几个实际项目中遇到的坑:
- 中断采样丢失:如果中断请求信号持续时间太短,CPU可能采样不到。我曾经在一个FPGA项目里,外设只拉高了一个时钟周期的中断信号,结果CPU根本没看到。解决方案是让中断信号保持至少一个指令周期。
- 中断向量表没初始化:这个错误太常见了。CPU查表的时候发现表项是空的或者指向了错误地址,直接跑飞。我建议你在系统启动时就把所有中断向量都初始化好,哪怕暂时用不到的也填一个默认处理函数。
- 中断嵌套死锁:高优先级中断里调用了低优先级中断才能释放的资源——死锁了。这种问题排查起来特别痛苦,因为不是每次都能复现。
我的习惯:每次写中断处理程序,我都会在入口处先保存所有用到的寄存器,出口处恢复。虽然编译器有时候会帮你做,但我从来不信编译器——自己动手,心里踏实。
好了,中断响应过程就讲到这里。记住四个关键词:采样、响应、识别、处理。把这四个阶段搞明白,中断这块你就通了八成。
公众号:蓝海资料掘金营,微信deep3321