1. 概述
在嵌入式系统中,外设(如 I2C 控制器)完成数据发送后,需要及时通知 CPU。为了避免 CPU "忙等待"浪费算力,现代计算机引入了中断(Interrupt)机制 。
本文档记录了从硬件引脚触发 到内核驱动执行的完整中断流转过程。
2. 核心角色定义
-
外设控制器(如 I2C):硬件事务的执行者,完成后产生电信号。
-
GIC(通用中断控制器):硬件模块,充当外设与 CPU 之间的"前台秘书",负责汇总和转发信号。
-
CPU:核心大脑,负责执行指令。
-
中断向量表:CPU 硬件级的路由表,指向总处理入口。
-
irq_desc数组:Linux 内核软件层维护的中断描述符表,记录具体的中断号、处理函数和状态。
3. 中断处理完整流程
第一步:外设产生硬件信号(触发)
-
动作 :当 I2C 控制器完成一段数据的物理发送后,其内部硬件电路会自动将分配给它的 IRQ 引脚拉高(产生电脉冲)。
-
比喻:快递员(I2C)送完货,按下了墙上的门铃。
第二步:GIC 接收并登记信号
-
动作 :GIC 收到 I2C 送来的高电平信号。GIC 内部有专属的硬件寄存器(如
GICD_ISPENDR,中断挂起状态寄存器)。它会立刻记录:"对应 32 号中断的引脚有信号拉高了!" -
动作 :GIC 根据内部配置,向 CPU 发送一个总的通知信号(拉高 CPU 的
nIRQ引脚)。 -
比喻:前台秘书(GIC)看到32号门铃亮了,在记事本上打个勾,然后拿起内线电话通知老板(CPU):"有人找"。
第三步:CPU 硬件强制打断并跳转
-
动作 :CPU 芯片硬件侦测到总信号。硬件电路强制暂停当前正在执行的进程(保存寄存器现场)。
-
动作:CPU 根据底层的"中断向量表",立刻跳转到预先写好的"硬件中断总入口"(比如 ARM 架构的 IRQ 入口代码)。
-
比喻:老板(CPU)立刻挂断当前电话,按下"中断接听键"。
第四步:获取具体中断号(向 GIC 索要)
-
动作 :在"中断总入口"的底层汇编/驱动代码中,CPU 并不知道具体是谁引发了中断。于是,CPU 向 GIC 的特定寄存器(如
GICC_IAR,中断响应寄存器)发起读取操作。 -
动作 :GIC 返回具体的中断号 (例如:返回数字
32)。 -
比喻:老板拿起内线问秘书:"刚才是哪个部门找我?"秘书回答:"是 32 号部门。"
第五步:Linux 内核分发(查软件表)
-
动作 :CPU 拿到
32这个数字后,不再直接查底层的硬件向量表,而是去查 Linux 内核在内存中维护的软件数组irq_desc[32]。 -
动作 :CPU 读取
irq_desc[32]结构体,发现里面绑定了具体的驱动处理函数:i2c_imx_isr()。 -
比喻:老板翻开桌面上的《部门通讯录》,翻到第 32 页,看到上面写着"负责处理人:张三(i2c_imx_isr)"。
第六步:执行驱动逻辑(唤醒睡眠线程)
-
动作 :CPU 跳转到
i2c_imx_isr()函数执行。该函数会清除 I2C 的硬件中断标志,并把刚才因为等待 I2C 数据而进入睡眠状态的线程 (状态从Sleep改为Runnable)。 -
动作:中断处理结束,CPU 恢复刚才被打断的进程,或者调度器切换到那个被唤醒的线程继续往下执行(比如应用层读取到了新数据,开始处理下一步逻辑)。
-
比喻:老板给张三(I2C驱动)打电话交代事情,办完后,老板想起刚才那个一直等结果的员工(睡眠线程),告诉他:"数据到了,你接着干吧"。
4. 总结
整个中断过程的核心在于**"软硬件解耦"**:
-
硬件层(外设 → GIC → CPU):负责以最快的物理速度传递信号,实现"打断"。
-
软件层(中断向量表 → irq_desc → ISR 函数):负责根据中断号,精准映射到具体的业务代码,实现"分发与处理"。
这种机制确保了无论外设的速度有多慢(如 I2C 的几十微秒),都不会阻塞 CPU 的高速运行;同时又能在数据准备的瞬间,以极高的实时性唤醒等待的线程。