1. 中断基础概念

中断(Interrupt)是硬件与软件之间的一种异步事件通知机制。当外设需要 CPU 处理数据或状态变化时,通过中断信号线向 CPU 发送一个电信号,CPU 暂停当前正在执行的指令流,转而执行预先注册好的中断服务程序(ISR),处理完成后再恢复原来的执行流。中断机制是操作系统实现多任务、实时响应和高性能 I/O 的基石。

1.1 什么是中断

中断的本质是 CPU 对异步事件的响应。与轮询(Polling)方式相比,中断避免了 CPU 持续占用资源去检查设备状态,从而大幅提升系统整体效率。

对比维度 中断方式 轮询方式
CPU 利用率 高,无事时 CPU 可执行其他任务 低,CPU 需不断检查设备状态
响应延迟 低,事件发生立即通知 高,取决于轮询间隔
实现复杂度 较高,需中断控制器和向量表 简单,仅需循环读取状态寄存器
适用场景 低频率、突发性事件(网卡、磁盘) 高频率、可预测事件(定时器)
关键认知: 中断不是“打断”而是“协作”。CPU 与设备通过中断信号实现高效的异步通信,中断处理程序必须短小精悍,否则会阻塞其他中断和进程调度。

1.2 中断向量与中断请求(IRQ)

中断向量(Interrupt Vector) 是一个数字编号,每个中断源对应一个唯一的向量号。CPU 收到中断信号后,根据向量号在中断描述符表(IDT)中查找对应的中断服务程序入口地址。在 x86 架构中,向量号范围通常为 0–255,其中 0–31 为 CPU 内部异常和 NMI,32–255 为外部硬件中断。

中断请求(IRQ) 是硬件设备向中断控制器(如 APIC、IO-APIC)发送的电信号。每个设备被分配一个 IRQ 号,中断控制器将 IRQ 映射为中断向量,并转发给 CPU。在 Linux 中,/proc/interrupts 文件列出了当前系统中所有注册的 IRQ 及其统计信息。

实践提示: 使用 cat /proc/interrupts 可以查看每个 CPU 核处理的中断次数分布,这是判断中断是否均衡的第一手数据。
# 示例:查看 /proc/interrupts 输出片段
           CPU0       CPU1       CPU2       CPU3
  0:         45          0          0          0   IO-APIC   2-edge      timer
  1:          3          0          0          0   IO-APIC   1-edge      i8042
  8:          1          0          0          0   IO-APIC   8-edge      rtc0
 24:     123456      98765      87654      76543   PCI-MSI 524288-edge      eth0
 25:          0          0          0          0   PCI-MSI 524289-edge      eth1

1.3 中断处理流程

一次完整的中断处理从硬件信号产生到软件处理结束,分为上半部(Top Half)和下半部(Bottom Half)两个阶段。上半部在中断上下文中执行,要求快速完成;下半部在软中断或 tasklet 中执行,可延迟处理。

中断处理完整流程 外设触发中断 中断控制器(APIC) CPU 响应中断 保存 CPU 上下文 查找 IDT 表 执行上半部(ISR) 调度下半部(Softirq) 上半部(Top Half)特点: • 在中断上下文中运行,不可睡眠 • 必须快速完成(通常 < 100μs) • 仅处理关键硬件操作,如清除中断标志 下半部(Bottom Half)特点: • 在软中断上下文中运行,可被高优先级中断抢占 • 处理数据拷贝、协议栈等耗时操作 • 可通过 tasklet、workqueue 等机制实现

1.4 中断处理的关键数据结构

Linux 内核使用以下核心数据结构管理中断:

  • irq_desc:每个 IRQ 对应一个 irq_desc 结构体,包含中断状态、处理函数链表、亲和性掩码等信息。
  • irqaction:描述一个中断处理动作,包含处理函数指针、设备名称、触发方式等。多个设备可共享同一 IRQ。
  • 中断描述符表(IDT):x86 架构中,IDT 将中断向量号映射到对应的门描述符,门描述符中包含段选择子和偏移量,指向 ISR 入口。
// Linux 内核 irq_desc 结构体简化定义
struct irq_desc {
    struct irq_data        irq_data;       // 中断数据,包含向量号、芯片信息
    struct irqaction      *action;         // 中断处理动作链表
    unsigned int           status_use_accessors; // 中断状态标志
    cpumask_var_t          affinity;       // CPU 亲和性掩码
    unsigned int           irq_count;      // 中断计数
    // ... 其他字段
};
常见误区: 中断向量号与 IRQ 号不是同一个概念。IRQ 号是设备在中断控制器上的逻辑编号,中断向量号是 CPU 在 IDT 中查找 ISR 的索引。IO-APIC 负责将 IRQ 映射为向量号,映射关系可通过 /proc/irq/{IRQ}/node 查看。

1.5 中断亲和性与负载均衡

每个 IRQ 都有一个 CPU 亲和性掩码(affinity mask),决定该中断可以被哪些 CPU 核处理。默认情况下,中断可能集中在 CPU0 上,导致单核负载过高。通过设置亲和性掩码,可以将中断分散到多个 CPU 核,实现中断负载均衡。

操作 命令/接口 说明
查看当前亲和性 cat /proc/irq/{IRQ}/smp_affinity 输出十六进制掩码,如 00000001 表示仅 CPU0
手动设置亲和性 echo 0f > /proc/irq/{IRQ}/smp_affinity 将中断分配给 CPU0-3(二进制 1111)
查看中断分布 cat /proc/interrupts 每列对应一个 CPU 核的中断计数
使用 irqbalance systemctl start irqbalance 自动动态调整中断亲和性
核心要点: 中断负载均衡的目标是让每个 CPU 核处理的中断数量大致相等,避免单核过载导致系统响应延迟增加。irqbalance 工具通过周期性采样中断分布和 CPU 负载,动态调整 IRQ 的亲和性掩码,是实现自动均衡的标准方案。