前提基于 linux+arm64+gic 框架
1. 中断类型(基于中断源划分)
| SGI | 软件写 GICD_SGIR 寄存器 | 0~15 | 多核核间同步 / 唤醒 |
| SPI(外设中断) | 硬件外设 | 32~1019 | 响应硬件事件 |
| PPI(私有中断) | CPU 核心私有外设(定时器) | 16~31 | 核心本地硬件事件 |
| 软中断(softirq) | 软件位图标记(无 GIC 参与) | 无固定编号 | 内核软件任务延迟处理 |
GIC 手册中对中断类型定义:
软件可以设置硬件中断的优先级可以设置, 写入gic 寄存器里面, 如果没有设置,gic同时收到俩个中断, gic会先分发哪个中断, 目前没有明确定义, 各家的芯片处理可能不一样。GIC可能是按照轮询来: 比如上次100号中断, 这次优先检查101
linux 系统的中断号按照GIC规范定义的的irq中断号为基础,然后进行映射。
linux 管理中断有三个结构体: irq_desc、irq_domain、irq_chip
irq_desc | 中断的 “总控档案”:每个软件中断号(irqnr)对应一个实例,管理该中断的处理逻辑、状态、统计,是中断管理的 “中枢”。 |
irq_domain | 中断号的 “翻译官”:将不同硬件控制器的 “硬件中断号(hwirq)” 映射为内核全局唯一的 “软件中断号(irqnr)”,是多控制器中断号的 “统一命名空间”。 |
irq_chip | 中断控制器的 “硬件驱动”:封装具体中断控制器(如 GICv2/GICv3、GPIO)的底层寄存器操作,是内核与硬件控制器的 “硬件无关性接口” |
2. 中断执行过程
- 硬件触发:通过 GPIO 或专用引脚向 GIC 发送中断请求(IRQ)。
- GIC 路由:GIC 根据中断配置(如中断号、优先级),将请求路由到指定 CPU 核心的 IRQ 引脚。
- CPU 异常入口:
- CPU 检测到中断信号,触发异常向量表(一般是start.S 最前面)中的中断入口(如
el1_irq)。 - arch/arm64/kernel$ vi entry.S
- 汇编代码执行:保存 CPU 上下文(x0-x31、SP、ELR_EL1)、关闭全局中断(CPSR.I=1)、跳转到内核 C 语言处理函数
handle_arch_irq。
- 汇编代码执行:保存 CPU 上下文(x0-x31、SP、ELR_EL1)、关闭全局中断(CPSR.I=1)、跳转到内核 C 语言处理函数
- 内核核心处理:
- el1h_64_irq_handler–》el1_interrupt (arch/arm64/kernel/entry-common.c)
- regs: 中断寄存器上下文 handler: 中断处理函数
- __el1_irq–》do_interrupt_handler--》 handler(regs); handler其实就是GIC 驱动的
gic_handle_irq函数的指针,读取 GIC 的 IRQ 号。 - (drivers/irqchip/irq-gic-v3.c/irq-gic.c 中驱动初始的时候设置的handler指向
gic_handle_irq) - gic_handle_irq 处理GICv3和GICv2 处理不一样
- GICv2 控制器的中断缓存能力比较弱, 所以会有一个while 循环查询当前gic 上的中断。如下图的do while循环
如果是GICv3 控制器:gic_handle_irq--》__gic_handle_irq_from_irqson--》__gic_handle_irq- generic_handle_domain_irq–》handle_irq_desc(这段代码调用就和之前gicv2 中断控制器是一样的)
- 通过
irq_to_desc(irq)获取对应的irq_desc结构体,检查中断状态(是否屏蔽、是否在处理中)。
- 顶半部执行:
- 调用
desc->handle_irq(如handle_level_irq/handle_edge_irq),执行注册的 ISR(request_irq注册的函数)。 - ISR 仅做紧急操作(如读取硬件寄存器数据到缓冲区),然后触发底半部(如
schedule_work)。
- 调用
- 底半部调度:
- 软中断 /tasklet 在中断返回时(
irq_exit)执行(关中断,适合短任务)。 - 工作队列由内核线程(kworker)执行(可调度,支持睡眠,适合长任务)。
- 软中断 /tasklet 在中断返回时(
- 上下文恢复:执行
irq_return,恢复 CPU 上下文,开启中断(CPSR.I=0),返回被中断的任务(或调度新任务)。
- CPU 检测到中断信号,触发异常向量表(一般是start.S 最前面)中的中断入口(如
关键内核函数调
/ 中断注册(车载驱动中常用) request_irq(irq, isr, IRQF_TRIGGER_RISING, "device_name", dev); // 中断核心处理函数 handle_arch_irq() --> gic_handle_irq() --> irq_to_desc() --> desc->handle_irq() // 底半部触发 schedule_work(&work); // 工作队列 tasklet_schedule(&tasklet); // tasklet raise_softirq(SIRQ_NET_TX); // 软中断