📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统》
🎥 更多学习视频请关注 B 站:嵌入式Jerry
高性能Linux中断全解析:开发者必备的系统、设备树和代码全套理解
在嵌入式或内核开发中,我们经常遇到“中断”和“异常”这两个词。很多人初学时会问:它们有什么区别?它们怎么触发?和驱动开发有何关系?
从架构上看:
- 异常(Exception) 是 CPU 在执行指令时检测到问题(如非法访问、除以 0、缺页、系统调用等)而主动触发的机制,属于同步事件。
- 中断(Interrupt) 则是来自外部硬件(如定时器、网卡、串口等)异步打断 CPU 的运行流程,属于异步事件。
它们虽然名字不同,但在 CPU 层面统一走异常向量机制,统一通过跳转到异常向量表指定的入口进行处理。而 Linux 内核也在此基础上做了完整封装,包括上半部处理、下半部调度、中断控制器抽象、设备树中断描述等模块。
理解这些基础,将帮助我们彻底掌握驱动中断处理、性能优化、调试分析的本质原理。
一些初学者对“中断、异常、上下文、终端、设备树”等概念感到混淆,实际上Linux很系统地封装了这些组织。本文给出一套完整、清晰、可培备的内核中断解析。
一、中断、异常:标准定义整理
概念 | 是否同步 | 是否来自外设 | 是否通过异常向量 | 示例 | 分类 |
---|---|---|---|---|---|
同步异常 | 是 | 否 | 是 | 除 0,Page Fault | Exception (异常) |
异步中断 | 否 | 是 | 是 | 网卡中断,定时器 | Interrupt (中断) |
软中断 | 否 | 否 | 否 | net_rx_action | 内核设计机制 |
【记忆口词:“异常是CPU自己出错,中断是别人打扰。同步是挂在自己脚上的队列,异步是不知不见的打中。”】
二、中断上下文是啥?上半部/下半部处理
名称 | 解释 |
---|---|
中断上下文 | CPU 正处于中断处理的环境,不允许睡眠、不允许调度 |
上半部 | IRQ handler,简单带走,引发软中断 |
下半部 | SoftIRQ / Tasklet / Workqueue 一系列延迟处理机制 |
流程图:Linux 中断处理全链路
外设中断信号 → [GIC/APIC] → [do_IRQ()]
→ IRQ Handler (e1000_intr)
→ raise_softirq(NET_RX)
→ do_softirq()
→ net_rx_action()
三、如何判断是否处于中断上下文?
函数 | 作用 |
---|---|
in_interrupt() | 判断是否处于中断上下文 |
in_irq() | 是否处于硬中断上下文 |
in_softirq() | 是否处于 SoftIRQ 上下文 |
in_task() | 是否处于进程上下文 (允许睡眠) |
四、异常向量:Exception 和 Interrupt 公用地址表
- CPU 给出 VBAR_ELx 系列定义异常向量基地址
- Exception 和 IRQ/FIQ 其实共用同一异常向量机制
ARMv8 举例:
异常类型 | 偏移 | 示例 |
---|---|---|
EL0 Sync | 0x200 | system call (svc) |
EL0 IRQ | 0x280 | 外设中断 |
EL1 Sync | 0x000 | kernel fault, undefined instr |
五、中断控制器和设备树配置
1. 中断控制器是 SoC 硬件模块
- ARM 经常是 GIC (Generic Interrupt Controller)
- RISC-V 是 PLIC
2. 设备树配置中断通道
interrupt-controller@38800000 {
compatible = "arm,gic-400";
reg = <0x38800000 0x1000>,
<0x38810000 0x1000>;
interrupt-controller;
#interrupt-cells = <3>;
};
该节点声明:这是一个 GIC 类型的中断控制器,使用 <3>
表示子设备使用中断时,需要 3 个单元描述一个中断:
- 第1项:中断类型(如 GIC_SPI = 0)
- 第2项:中断号(如 122)
- 第3项:触发类型(如 IRQ_TYPE_LEVEL_HIGH = 4)
设备节点中通过 interrupt-parent
关联该中断控制器,并指定中断号:
ethernet@5b000000 {
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>;
};
✅ 表示该以太网设备使用 GIC 控制器的 SPI 类型第 122 号中断,采用高电平触发。
如果设备有多个中断源,也可以写多个中断元组:
interrupts = <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
设备树 → 中断控制器 → 内核解析 → request_irq → 驱动处理函数,一条链条完全连接。
六、实战示例:IRQ 处理函数全链路
1. 注册 IRQ
request_irq(122, my_irq_handler, 0, "my_eth", NULL);
2. 处理函数
static irqreturn_t my_irq_handler(int irq, void *dev_id)
{
pr_info("IRQ %d received\n", irq);
/* 发起软中断 */
raise_softirq(NET_RX_SOFTIRQ);
return IRQ_HANDLED;
}
3. 软中断处理
static void net_rx_action(struct softirq_action *a)
{
/* 处理网络数据包 */
}
七、总结记忆图:异常和中断规范结构
Exception (异常)
/ \
Synchronous Exception Asynchronous Exception
(系统调用、除 0) (外设 IRQ中断)
|
异常向量
|
[调用异常处理函数]
|
[触发 SoftIRQ/下半部]
|
[net_rx_action / workqueue]
结言
解决中断、异常、上下文、硬软分层等概念混淆,必须理清三者分别:
- 异常 (Exception):包括 系统调用、PageFault,由指令触发
- 中断 (Interrupt):外设硬件打断,是异步异常
- 软中断:内核框架下半部,非 CPU 异常系统
通过设备树 + GIC 硬件 + Linux IRQ 进入热点实战,方可真正理解 Linux 高性能应用中断细节。
📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统》
🎥 更多学习视频请关注 B 站:嵌入式Jerry