中断机制基础:中断控制器、向量表与上下文切换

中断是嵌入式系统响应外部事件的硬件级信号机制。在网关设备中,网络数据包到达、DMA传输完成、定时器溢出等事件均通过中断通知CPU。理解中断控制器(如GIC)的工作原理、中断向量表的结构以及中断上下文与进程上下文的切换开销,是优化中断延迟和DMA带宽的前提。

1. 中断控制器(GIC)工作原理

通用中断控制器(Generic Interrupt Controller, GIC)是ARM架构中标准的中断管理单元,负责接收、优先级排序、分发中断信号给CPU核心。GIC主要分为两个逻辑单元:分发器(Distributor)和CPU接口(CPU Interface)。

  • 分发器:接收所有外设中断源(SPI、PPI、SGI),管理中断优先级、使能/禁用、触发方式(电平/边沿),并将最高优先级的中断路由到指定的CPU核心。
  • CPU接口:每个CPU核心拥有独立的CPU接口,负责向核心发送中断请求信号(IRQ/FIQ),并处理中断确认(ACK)和结束(EOI)流程。
关键指标:GIC-400(常见于Cortex-A7/A9)支持最多1024个中断源,每个中断源可配置256级优先级。中断延迟从外设触发到CPU执行第一条中断指令通常为50~200个CPU周期。
性能调优提示:在网关驱动中,将网络RX/TX中断设置为最高优先级(0),并绑定到特定CPU核心(中断亲和性),可显著减少中断响应抖动。
GIC中断控制器架构与分发流程 外设中断源 (SPI/PPI/SGI) 分发器 优先级排序 使能/触发配置 CPU接口0 IRQ/FIQ CPU接口1 IRQ/FIQ CPU0 核心 CPU1 核心 中断分发流程说明 1. 外设触发中断信号(电平/边沿)发送至分发器。 2. 分发器根据优先级寄存器选择最高优先级中断,并查询目标CPU路由表。 3. 分发器将中断ID和优先级信息发送至目标CPU的CPU接口。 4. CPU接口向核心发送IRQ信号,核心响应后读取GICC_IAR寄存器获取中断ID。 5. 中断处理完成后,核心写入GICC_EOIR寄存器通知GIC中断结束。

2. 中断向量表与中断处理流程

中断向量表(Interrupt Vector Table, IVT)是存储在固定内存地址的表,每个表项对应一个中断源,存放该中断的服务例程(ISR)入口地址。当CPU响应中断时,硬件自动跳转到向量表中对应的地址执行。

中断类型 向量偏移 典型用途 优先级范围
复位(Reset) 0x0000 系统启动入口 最高(-1)
未定义指令 0x0004 异常处理 高
软件中断(SWI) 0x0008 系统调用 中
预取中止 0x000C 指令访问错误 高
数据中止 0x0010 数据访问错误 高
IRQ(普通中断) 0x0018 外设中断(网卡、DMA等) 可配置(0~255)
FIQ(快速中断) 0x001C 高速/低延迟中断 最高(0)

中断处理流程分为硬件自动完成和软件处理两个阶段:

  1. 硬件阶段:CPU检测到IRQ/FIQ信号 → 保存当前程序状态(CPSR到SPSR) → 切换到中断模式 → 禁用同级中断 → 从向量表加载PC(跳转到ISR入口)。
  2. 软件阶段:ISR入口保存现场(通用寄存器、LR) → 读取中断ID(GICC_IAR) → 调用具体中断处理函数 → 处理完成后写EOI → 恢复现场 → 执行中断返回指令(SUBS PC, LR, #4)。
常见陷阱:中断向量表必须放置在CPU规定的基地址(如0xFFFF0000或0x00000000),且每个表项对齐到4字节。在Linux内核中,中断向量表由arch/arm/kernel/entry-armv.S汇编代码管理,驱动开发者通常不直接修改IVT,而是通过request_irq()注册中断处理函数。
中断处理流程:硬件阶段 + 软件阶段 硬件阶段(CPU自动完成) 1. CPU检测IRQ/FIQ信号 2. 保存CPSR到SPSR_irq 3. 从向量表加载PC(跳转ISR) 软件阶段(ISR执行) 4. 保存现场(寄存器入栈) 5. 读取GICC_IAR获取中断ID 6. 调用具体中断处理函数 7. 写GICC_EOIR结束中断 8. 恢复现场,执行SUBS PC, LR, #4 中断延迟关键路径 中断延迟 = 硬件识别延迟 + 向量表跳转延迟 + 现场保存延迟 + ISR执行时间 + 现场恢复延迟 优化重点:减少ISR执行时间(使用中断线程化/下半部),避免在ISR中调用耗时函数。

3. 中断上下文与进程上下文切换开销

在Linux内核中,中断处理程序运行在中断上下文(Interrupt Context)中,与普通的进程上下文(Process Context)有本质区别。理解两者的差异对于编写高性能驱动至关重要。

对比维度 中断上下文 进程上下文
运行模式 IRQ/FIQ模式(特权级) SVC/User模式
可睡眠/调度 ❌ 不可睡眠,不可调用schedule() ✅ 可睡眠,可被抢占
可访问用户空间 ❌ 不可访问(current指针无效) ✅ 可访问用户态内存
栈空间 每个CPU独立的中断栈(通常4KB~16KB) 进程内核栈(通常8KB)
锁使用限制 只能使用spinlock,不可使用mutex 可使用mutex、semaphore等睡眠锁
典型切换开销 ~0.5~2 µs(仅保存/恢复寄存器) ~5~20 µs(含TLB刷新、MMU切换)
性能数据:在1GHz Cortex-A72平台上,一次中断上下文切换(从用户态进程到ISR再返回)的纯硬件开销约为0.8~1.5 µs。如果ISR中使用了耗时操作(如打印日志、复杂计算),延迟可能膨胀到10 µs以上,直接影响网络吞吐量。

上下文切换开销的组成:

  • 寄存器保存/恢复:通用寄存器(R0-R12)、LR、SP、SPSR等,约20~30个寄存器入栈/出栈。
  • Cache/TLB污染:中断处理会引入新的指令和数据缓存行,可能冲刷掉进程上下文的热数据。
  • 中断嵌套:高优先级中断打断低优先级ISR,导致额外的保存/恢复开销。
  • 中断线程化:将中断处理推迟到内核线程中执行,虽然增加了调度延迟,但减少了中断上下文中的耗时操作。
优化策略:对于网关驱动中的高速数据路径(如10Gbps网卡),建议使用NAPI(New API)结合轮询模式,减少中断频率;对于必须使用中断的场景,将数据拷贝和协议处理放到下半部(tasklet/workqueue)中执行,ISR只做最小必要操作(如读取DMA描述符状态)。
中断上下文 vs 进程上下文切换开销对比 中断上下文切换 保存R0-R12, LR, SP, SPSR (约28个寄存器) 切换至IRQ模式栈(无需TLB刷新) 执行ISR → 恢复现场 → 返回 进程上下文切换 保存全部寄存器 + 浮点/NEON状态 切换页表基地址(TLB刷新) 切换内核栈 → 调度器执行 → 恢复新进程 典型开销对比(1GHz Cortex-A72) 中断上下文: 0.8~1.5 µs 进程上下文: 5~20 µs → 中断切换开销约为进程切换的 1/10 ~ 1/20 → 但中断上下文不可睡眠,限制了可执行的操作类型

4. 中断延迟测量与优化方向

中断延迟(Interrupt Latency)定义为从外设触发中断信号到CPU开始执行ISR第一条指令的时间间隔。在网关驱动中,中断延迟直接影响网络数据包的接收及时性。

中断延迟的组成公式:

中断延迟 = 硬件传播延迟 + GIC仲裁延迟 + CPU响应延迟 + 现场保存延迟

硬件传播延迟:信号从外设到GIC的物理线路延迟(通常 < 100 ns)
GIC仲裁延迟:优先级排序和路由决策时间(通常 200~500 ns)
CPU响应延迟:当前指令完成 + 模式切换(通常 100~300 ns)
现场保存延迟:寄存器入栈时间(取决于栈速度和寄存器数量,通常 200~800 ns)
实测数据:在i.MX8M Plus平台(4×Cortex-A53 @1.6GHz)上,使用GPIO触发中断,实测最小中断延迟约0.9 µs,平均1.2 µs,最大抖动±0.3 µs。当系统负载较高(Cache压力大)时,最大延迟可能达到3~5 µs。

优化方向总结:

  • 中断亲和性绑定:将特定中断绑定到专用CPU核心,避免多核竞争GIC资源。
  • 中断优先级配置:将关键中断(如网卡RX)设置为最高优先级,减少被其他中断打断的概率。
  • 中断合并(Coalescing):在中断控制器或网卡侧合并多个中断,减少中断频率,但会增加单次延迟。
  • 使用FIQ:对于极致低延迟需求(如PTP时间同步),使用FIQ代替IRQ,FIQ拥有独立的寄存器组,减少现场保存开销。
  • 中断线程化:将非关键处理移至内核线程,降低ISR执行时间,但会增加调度延迟。
调试工具:使用tracepoint irq_handler_entry/irq_handler_exit通过ftrace测量中断延迟;使用/proc/interrupts查看中断分布和CPU亲和性;使用perf stat -e irq_vectors:*统计中断向量事件。