2. Linux中断子系统架构
Linux 中断子系统是内核响应硬件事件的枢纽。在 ARM 多核平台上,通用中断控制器 (GIC) 是核心硬件单元,负责中断的仲裁、分发与优先级管理。内核通过 中断描述符 (irq_desc) 将硬件中断号映射为软件可管理的资源,并按照 上半部 (硬中断) → 下半部 (软中断/tasklet/workqueue) 的流水线处理流程完成响应。
2.1 中断控制器 (GIC) 架构
GIC (Generic Interrupt Controller) 是 ARM 体系的标准中断控制器,当前主流为 GICv2/GICv3。GIC 将中断分为三类:
| 中断类型 | 触发方式 | 典型用途 | GIC 处理方式 |
|---|---|---|---|
| SPI (Shared Peripheral Interrupt) | 电平/边沿 | 外设中断(网卡、USB、UART) | 可路由到任意 PE,支持多核分发 |
| PPI (Private Peripheral Interrupt) | 电平/边沿 | 每个 CPU 核私有(如本地定时器) | 仅由绑定的 PE 处理 |
| SGI (Software Generated Interrupt) | 边沿 | 核间通信 (IPI) | 写 GICD_SGIR 寄存器触发 |
GIC 内部由两大逻辑单元构成:分发器 (Distributor) 和 CPU 接口 (CPU Interface)。分发器负责中断优先级排序、使能/屏蔽、路由策略;CPU 接口负责将最高优先级中断信号传递给具体 CPU 核。
2.2 中断描述符 (irq_desc)
内核使用 struct irq_desc 结构体管理每个中断源。每个硬件中断号 (hwirq) 对应一个中断描述符,描述符中记录了中断的触发类型、处理函数链、状态标志、芯片操作集等信息。
// include/linux/irqdesc.h (简化结构)
struct irq_desc {
struct irq_common_data irq_common_data;
struct irq_data irq_data;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq; // 中断流处理函数
struct irqaction *action; // 中断动作链表
unsigned int status_use_accessors;
struct irq_chip *chip; // 芯片级操作
struct irq_domain *irq_data.domain; // 中断域映射
const char *name;
};
中断描述符通过 irq_domain 实现硬件中断号到 Linux irq 号的映射。irq_domain 支持三种映射方式:
- 线性映射 (Linear):适用于连续 hwirq 范围,直接数组索引,效率最高。
- 树形映射 (Tree):适用于稀疏 hwirq,使用基数树存储,节省内存。
- 不映射 (No Map):由驱动自行管理 irq 号分配,灵活性最高。
action 字段是一个链表,支持多个设备共享同一中断线。共享中断的驱动必须调用 request_irq() 时设置 IRQF_SHARED 标志,并在中断处理函数中检测设备是否真正产生中断。
2.3 中断处理流程
当 CPU 收到 GIC 发出的 IRQ 信号后,内核执行以下标准流程:
- 异常向量入口:CPU 跳转到
__irq_svc或__irq_usr汇编入口,保存现场。 - irq_enter:进入硬中断上下文,递增
preempt_count,禁止抢占。 - handle_arch_irq:调用架构相关的顶层中断处理函数,读取 GIC 的 IAR 寄存器获取中断号。
- generic_handle_irq:根据 irq 号找到对应的
irq_desc,调用handle_irq流处理函数。 - 流处理函数:根据中断类型调用
handle_edge_irq或handle_level_irq,执行action链表中的驱动注册函数。 - irq_exit:退出硬中断上下文,检查是否有软中断待处理,若有则调用
do_softirq。 - 恢复现场:执行
eret指令返回被中断的上下文。
handle_arch_irq 到 generic_handle_irq 的路径是优化重点,ARM 平台通过 gic_handle_irq 实现。
2.4 中断流处理函数与中断芯片操作
中断流处理函数 (irq_flow_handler_t) 是内核为不同触发类型提供的标准处理模板。常见流处理函数包括:
| 流处理函数 | 适用中断类型 | 行为特点 |
|---|---|---|
handle_simple_irq |
简单中断 | 直接调用 action,不进行屏蔽/确认操作 |
handle_level_irq |
电平触发 | 屏蔽中断 → 调用 action → 确认电平 → 解除屏蔽 |
handle_edge_irq |
边沿触发 | 屏蔽中断 → 调用 action → 确认边沿 → 解除屏蔽 |
handle_fasteoi_irq |
GIC 快速 EOI | 直接调用 action,依赖硬件自动 EOI |
中断芯片操作 (irq_chip) 封装了底层硬件寄存器操作,包括 irq_mask、irq_unmask、irq_ack、irq_eoi、irq_set_type 等回调。GIC 驱动通过 irq_gic_chip 结构体注册这些操作。
handle_fasteoi_irq 可减少寄存器读写次数,因为 EOI 操作由硬件自动完成,无需软件显式确认。
2.5 中断上下文与下半部衔接
中断处理分为上半部 (hardirq) 和下半部 (softirq)。上半部在关闭本地中断的上下文中执行,要求快速完成;下半部在开中断环境下执行,适合处理耗时操作。
内核提供三种下半部机制:
- 软中断 (Softirq):静态分配,编译时确定,用于高频率任务(如网络 RX/TX、定时器)。
- Tasklet:基于软中断实现,动态注册,同一 tasklet 串行执行。
- Workqueue:运行在进程上下文,可睡眠,适合阻塞操作。
中断处理流程中,irq_exit() 函数会检查 local_softirq_pending(),若有待处理的软中断则调用 do_softirq(),从而完成上半部到下半部的切换。