1. 中断基础概念:中断的定义、中断类型、中断上下文、中断优先级与嵌套

各位做嵌入式开发的朋友,咱们今天聊聊中断。说实话,中断这玩意儿是内核里最基础也最容易出坑的地方。我刚开始做驱动时,就因为对中断上下文理解不到位,踩过不少坑。今天我把这些基础概念掰开揉碎了讲清楚。

1.1 中断到底是什么?

中断,说白了就是CPU正在干一件事,突然被外部或内部的一个信号打断,转去处理更紧急的事情。处理完再回来接着干原来的活。这就像你正在写代码,突然电话响了——你得先接电话,接完再继续写。

从硬件角度看,中断是外设向CPU发送的一个电信号。从软件角度看,它是内核响应异步事件的一种机制。我个人习惯把中断理解为「硬件主动通知CPU:我有事找你,你停一下手头的活」。

核心要点:中断是异步事件处理机制,它让CPU不用轮询等待外设状态,大大提高了系统效率。

1.2 中断类型:IRQ、FIQ、NMI

ARM架构下,中断主要分三种。我当年在移植Linux内核到某款ARM芯片时,就因为这三种中断的配置搞错过一次,导致系统频繁死机。咱们一个个说。

中断类型 全称 特点 典型用途
IRQ Interrupt Request 普通中断,可被屏蔽 外设中断(网卡、串口等)
FIQ Fast Interrupt Request 快速中断,优先级高,有独立寄存器 高速数据传输、实时性要求高的场景
NMI Non-Maskable Interrupt 不可屏蔽中断,优先级最高 看门狗、硬件故障、电源异常

IRQ 是最常见的中断类型。你写的驱动里,大部分外设中断都是IRQ。它可以通过CPU的CPSR寄存器中的I位来屏蔽。

FIQ 在ARM里是个特殊存在。它有自己的影子寄存器组,处理时不需要保存和恢复普通寄存器,所以速度更快。我记得在做一个高速数据采集项目时,就专门把ADC的中断配成了FIQ,才满足了采样率要求。

NMI 这玩意儿,嗯,你想想看,连CPU都屏蔽不了它。一旦触发,CPU必须立即响应。我在项目中用NMI处理过电源掉电保护——电压一掉,NMI立刻触发,CPU赶紧保存关键数据到Flash里。

避坑指南:我曾经在配置FIQ时,忘记把中断向量表里的FIQ入口地址设置正确,结果一触发FIQ就跳飞了。调试了两天才发现是链接脚本的问题。所以,用FIQ时一定要检查向量表配置。

1.3 中断上下文

这个概念特别重要。很多驱动bug都出在对中断上下文的理解上。什么是中断上下文?说白了就是CPU当前处于中断处理程序执行期间的状态。

中断上下文有几个关键特征:

  • 不可睡眠:中断上下文里不能调用可能导致睡眠的函数,比如kmalloc(GFP_KERNEL)、mutex_lock()等。为什么?因为中断上下文没有进程调度实体,睡眠了没人能唤醒它。
  • 不可调度:中断处理期间,CPU不会主动让出执行权给其他进程。
  • 栈空间有限:每个CPU的中断栈通常只有几KB,别在中断里搞大数组或递归。

警告:我见过有人在中断处理函数里调用printk打印大量调试信息,结果栈溢出导致系统崩溃。printk本身没问题,但格式化字符串时可能会用栈空间。建议中断里只做最必要的事,复杂操作放到下半部(tasklet或workqueue)去做。

判断当前是否在中断上下文,可以用内核提供的API:

// 检查是否在中断上下文
if (in_interrupt()) {
    // 当前在中断上下文,不能睡眠
    // 只能用GFP_ATOMIC分配内存
    data = kmalloc(size, GFP_ATOMIC);
} else {
    // 进程上下文,可以睡眠
    data = kmalloc(size, GFP_KERNEL);
}

1.4 中断优先级与嵌套

中断优先级,说白了就是给中断排个队。高优先级的中断可以打断低优先级的中断处理。这就是中断嵌套。

在ARM Cortex-A系列处理器中,中断优先级由GIC(Generic Interrupt Controller)管理。GIC支持最多256个优先级(实际芯片可能只实现部分)。

中断嵌套的规则:

  • 高优先级中断可以打断低优先级中断的处理
  • 同级中断不能互相打断
  • 低优先级中断不能打断高优先级中断

我举个例子:假设系统里有三个中断,优先级分别是1(最高)、5、10(最低)。

  1. CPU正在处理优先级10的中断
  2. 此时优先级5的中断来了——它会打断优先级10的处理,CPU转去处理优先级5
  3. 在优先级5的处理过程中,优先级1的中断来了——它又会打断优先级5,CPU转去处理优先级1
  4. 优先级1处理完,回到优先级5继续处理
  5. 优先级5处理完,再回到优先级10继续处理

这就是中断嵌套的典型流程。但要注意,嵌套层数不能太多,否则栈空间会爆。我曾经在一个项目中,因为中断嵌套层数达到5层,导致中断栈溢出,系统随机死机。查了整整一周才定位到问题。

经验之谈:我个人建议,除非有严格的实时性要求,否则尽量少用中断嵌套。把中断处理做得短而快,复杂逻辑放到下半部去处理。这样既保证了实时性,又避免了嵌套带来的栈溢出风险。

1.5 本章知识体系

下面这张图展示了中断基础概念的核心逻辑关系,我画出来方便你理解:

中断基础概念知识体系 中断 中断定义 中断类型 中断上下文 优先级与嵌套 异步事件 硬件通知CPU IRQ(可屏蔽) FIQ(快速) NMI(不可屏蔽) 不可睡眠 栈空间有限 GIC管理优先级 高优先级打断低优先级 嵌套层数有限 中断是嵌入式系统响应外部事件的基石,理解这些概念是写好驱动的第一步

好了,中断的基础概念就讲到这里。这些内容看似简单,但每个点背后都有不少坑。你写驱动时,多想想当前代码在什么上下文中执行,中断优先级怎么配,嵌套会不会出问题。把这些基础打牢了,后面移植中断子系统时才能游刃有余。

小建议:刚开始学中断时,可以在开发板上写个简单的中断驱动——比如按一下按键触发中断,在中断里点亮一个LED。亲手跑一遍,比看十遍书都管用。


专注资料整理