2. Linux中断子系统架构

Linux 中断子系统是内核响应硬件事件的枢纽。在 ARM 多核平台上,通用中断控制器 (GIC) 是核心硬件单元,负责中断的仲裁、分发与优先级管理。内核通过 中断描述符 (irq_desc) 将硬件中断号映射为软件可管理的资源,并按照 上半部 (硬中断) → 下半部 (软中断/tasklet/workqueue) 的流水线处理流程完成响应。

核心要点:中断子系统将硬件中断信号转化为内核可调度的执行流,GIC 负责物理信号管理,中断描述符负责软件抽象,处理流程负责执行上下文切换。

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 核。

GICv3 新特性:引入 Redistributor 架构,每个 PE 拥有独立的 Redistributor,支持 LPI (Locality-specific Peripheral Interrupt) 和 ITS (Interrupt Translation Service),适合大规模虚拟化场景。
GIC 架构内部结构图 外设 SPI 私有 PPI 软件 SGI 分发器 优先级排序 使能/屏蔽 路由策略 中断分组 安全扩展 CPU 接口 中断确认 优先级掩码 抢占控制 EOI 信号 运行优先级 PE CPU GIC 中断流 外设中断 → 分发器排序 → CPU 接口仲裁 → 目标 PE 响应 → 内核中断处理

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 标志,并在中断处理函数中检测设备是否真正产生中断。
中断描述符与 irq_domain 映射关系 HW IRQ 32 HW IRQ 33 HW IRQ 34 irq_domain 线性映射 树形映射 不映射 hwirq → virq irq_desc[32] virq = 32 irq_desc[33] virq = 33 irq_desc[34] virq = 34 映射关系 hwirq 32 → virq 32 hwirq 33 → virq 33 hwirq 34 → virq 34 线性映射 偏移量 = 0

2.3 中断处理流程

当 CPU 收到 GIC 发出的 IRQ 信号后,内核执行以下标准流程:

  1. 异常向量入口:CPU 跳转到 __irq_svc 或 __irq_usr 汇编入口,保存现场。
  2. irq_enter:进入硬中断上下文,递增 preempt_count,禁止抢占。
  3. handle_arch_irq:调用架构相关的顶层中断处理函数,读取 GIC 的 IAR 寄存器获取中断号。
  4. generic_handle_irq:根据 irq 号找到对应的 irq_desc,调用 handle_irq 流处理函数。
  5. 流处理函数:根据中断类型调用 handle_edge_irq 或 handle_level_irq,执行 action 链表中的驱动注册函数。
  6. irq_exit:退出硬中断上下文,检查是否有软中断待处理,若有则调用 do_softirq。
  7. 恢复现场:执行 eret 指令返回被中断的上下文。
关键路径:从 GIC 触发到驱动 handler 执行,典型延迟在微秒级。其中 handle_arch_irq 到 generic_handle_irq 的路径是优化重点,ARM 平台通过 gic_handle_irq 实现。
Linux 中断处理完整流程 ① 外设触发中断 ② GIC 分发/仲裁 ③ CPU 异常向量入口 ④ irq_enter / 保存现场 ⑤ handle_arch_irq ⑥ generic_handle_irq ⑦ 驱动 handler 执行 ⑧ irq_exit / 恢复现场 流程说明 ① 外设拉高中断请求线 ② GIC 分发器优先级排序 ③ CPU 进入 IRQ 模式 ④ 禁止抢占,保存寄存器 ⑤ 读 IAR 获取中断号 ⑥ 查找 irq_desc 调用流处理 ⑦ 执行驱动注册的 handler ⑧ 写 EOI,检查软中断 恢复现场,eret 返回

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(),从而完成上半部到下半部的切换。

性能陷阱:上半部中不应执行耗时操作(如大块内存拷贝、互斥锁等待),否则会阻塞其他中断和实时任务。建议将非紧急处理移至 tasklet 或 workqueue。
上半部与下半部衔接流程 上半部 (Hardirq) 关中断执行 快速、不可睡眠 irq_exit() 检查软中断待处理 下半部 (Softirq) 开中断执行 可被硬中断抢占 Tasklet 串行执行 Workqueue 进程上下文 软中断 静态分配 ← 上一章 📖 返回目录 下一章 →