1. 中断基础概念:中断向量、中断优先级、中断嵌套、中断上下文

在网关驱动开发中,中断是CPU与外部设备(如以太网PHY、USB控制器、PCIe桥片)交互的核心机制。理解中断的基础概念,是排查“中断丢失”与“DMA超时”问题的前提。本节从四个维度展开:中断向量、中断优先级、中断嵌套与中断上下文。

1.1 中断向量

中断向量是中断服务程序(ISR)的入口地址索引。当外设触发中断时,CPU通过中断控制器(如ARM GIC、x86 APIC)查找向量表,跳转到对应的处理函数。

概念 说明 典型值/范围
中断向量号 每个中断源分配的唯一数字标识 0~255(x86),16~1019(ARM GICv3)
向量表基地址 存放中断向量入口地址的内存起始位置 0xFFFF0000(ARM),IDT(x86)
共享中断 多个设备共用同一个中断向量号 常见于PCIe MSI/MSI-X
排查要点:中断丢失常因向量号冲突或向量表被意外修改导致。使用 cat /proc/interrupts 可查看当前系统中每个中断号的触发次数与绑定CPU。
ARM64 中断向量表示例(Linux 内核代码片段):
// arch/arm64/kernel/entry.S
.align 11
SYM_CODE_START(vectors)
    ventry  el1t_sync_invalid        // Synchronous EL1t
    ventry  el1t_irq_invalid         // IRQ EL1t
    ventry  el1t_fiq_invalid         // FIQ EL1t
    ventry  el1t_error_invalid       // Error EL1t

    ventry  el1h_sync                // Synchronous EL1h
    ventry  el1h_irq                 // IRQ EL1h
    ventry  el1h_fiq_invalid         // FIQ EL1h
    ventry  el1h_error_invalid       // Error EL1h
    // ... 更多向量入口
SYM_CODE_END(vectors)
中断向量表结构示意图 中断向量表结构 向量表基地址 向量 #0 向量 #1 向量 #2 ... 向量 #N ISR_Handler_0 ISR_Handler_1 ISR_Handler_N 关键点 • 向量号由硬件决定 • 共享中断需遍历ISR • 向量表需对齐(ARM: 2KB)

1.2 中断优先级

中断优先级决定了当多个中断同时到达时,CPU优先响应哪一个。在网关设备中,高优先级中断通常分配给实时性要求高的外设(如PTP时间同步、看门狗),低优先级中断分配给批量数据传输(如DMA完成中断)。

中断控制器 优先级范围 优先级配置方式 典型应用
ARM GICv2/v3 0~255(数值越小优先级越高) ICC_PMR / GICD_IPRIORITYR 网关SoC(i.MX8, MT7621)
x86 APIC 0~15(数值越大优先级越高) IOAPIC RTE 寄存器 x86网关(Intel Celeron)
RISC-V PLIC 0~7 PLIC_PRIORITY 寄存器 RISC-V 网关(GD32V)
常见陷阱:在网关驱动中,若DMA完成中断优先级低于网络接收中断,当网络流量大时,DMA中断可能被长时间延迟,导致DMA超时。排查时应检查中断优先级分配是否合理。
中断优先级抢占示意图 中断优先级抢占执行流程 t0 t1 t2 t3 低优先级 ISR (Prio=5) 高优先级 ISR (Prio=1) 低优先级 ISR 恢复 高优先级中断(红色)抢占正在执行的低优先级中断(蓝色),完成后恢复低优先级中断执行

1.3 中断嵌套

中断嵌套是指一个中断服务程序执行过程中,被另一个更高优先级的中断打断。在网关驱动中,合理的中断嵌套可以保证紧急事件及时处理,但过度嵌套会导致栈溢出或优先级反转。

中断嵌套的三种模式:
  1. 完全嵌套:高优先级中断可以打断任何低优先级中断。ARM GIC 默认模式。
  2. 组嵌套:只有属于更高优先级组的中断才能打断当前中断。常用于安全关键系统。
  3. 禁止嵌套:所有中断按顺序执行,不互相打断。适用于简单嵌入式系统。
模式 优点 缺点 网关适用场景
完全嵌套 实时性最好 栈深度大,调试困难 多网口网关,需快速响应
组嵌套 安全隔离性好 配置复杂 工业网关,需功能安全
禁止嵌套 实现简单,栈可控 实时性差 低端IoT网关
中断嵌套栈变化示意图 中断嵌套栈变化 主程序 (Normal) ISR_Level_1 (Prio=3) ISR_Level_2 (Prio=1) 当前执行 (SP) 栈增长方向 嵌套执行顺序 1. 主程序执行中 2. 低优先级中断到达 3. 高优先级中断抢占 4. 高优先级ISR完成 5. 低优先级ISR恢复

1.4 中断上下文

中断上下文是CPU在执行中断服务程序时所处的特殊运行环境。与进程上下文不同,中断上下文不能睡眠、不能调度、不能访问用户空间。理解中断上下文是编写安全、高效ISR的基础。

对比项 进程上下文 中断上下文
可睡眠 是(通过 schedule()) 否(会导致内核崩溃)
可访问用户空间 是 否(mm_struct 可能无效)
持有锁 互斥锁、自旋锁 仅自旋锁(且需关中断)
栈空间 进程内核栈(8KB~16KB) 中断栈(4KB~8KB,独立或共用)
典型耗时 无限制 应 < 100μs(否则触发软中断/工作队列)
排查要点:中断丢失的常见原因之一是ISR中执行了耗时操作(如打印、内存分配),导致后续中断被屏蔽或丢失。应使用 in_irq() 宏判断当前是否在中断上下文中,并避免调用可能睡眠的函数。
Linux 内核中断上下文检测代码:
#include <linux/preempt.h>
#include <linux/hardirq.h>

void my_isr_handler(int irq, void *dev_id)
{
    // 检测是否在中断上下文中
    if (in_interrupt()) {
        pr_debug("当前在中断上下文中 (hardirq=%d, softirq=%d)\n",
                 in_irq(), in_softirq());
    }

    // 错误示例:在中断上下文中调用可能导致睡眠的函数
    // kmalloc(size, GFP_KERNEL);  // 错误!GFP_KERNEL 可能睡眠

    // 正确做法:使用 GFP_ATOMIC
    void *buf = kmalloc(64, GFP_ATOMIC);
    if (!buf) {
        pr_err("中断上下文内存分配失败\n");
        return;
    }

    // 处理中断...
    kfree(buf);
}
中断上下文与进程上下文切换 中断上下文与进程上下文切换 进程上下文 可睡眠 / 可调度 中断发生 中断上下文 不可睡眠 / 不可调度 中断返回 进程恢复 继续执行 中断上下文关键约束 • 不能调用 schedule()、wait_event()、mutex_lock() 等可能睡眠的函数 • 不能访问用户空间内存(copy_from_user 会触发 page fault) • 应使用 spin_lock_irqsave() 而非 mutex_lock() 保护共享数据

1.5 本节小结