1. 中断基础
中断是硬件与操作系统之间异步通信的核心机制。当外设(如网卡)需要 CPU 处理数据时,通过中断信号通知 CPU 暂停当前任务,转而执行中断服务程序(ISR)。理解中断的底层原理是优化网络驱动性能的前提。
1.1 中断的基本概念
中断允许 CPU 在等待外设操作时执行其他任务,从而提升系统吞吐量。中断分为同步中断(异常)和异步中断(硬件中断)。在网络驱动场景中,我们主要关注硬件中断。
关键术语:
- IRQ(Interrupt ReQuest):中断请求线,每个外设或中断源分配一条 IRQ 编号。
- ISR(Interrupt Service Routine):中断服务程序,内核注册的处理函数。
- 中断上下文:ISR 运行在中断上下文中,不可睡眠,不能调用可能导致阻塞的函数。
- 中断延迟:从硬件产生中断信号到 CPU 开始执行 ISR 的时间间隔。
1.2 中断向量表(IVT / IDT)
中断向量表是 CPU 用于查找中断处理程序入口地址的数据结构。在 x86 架构中,实模式下称为 IVT,保护模式和长模式下称为 IDT(Interrupt Descriptor Table)。
| 模式 | 名称 | 表项大小 | 最大中断数 | 基址寄存器 |
|---|---|---|---|---|
| 实模式 | IVT | 4 字节(段:偏移) | 256 | 固定 0x0000 |
| 保护模式 | IDT | 8 字节(门描述符) | 256 | IDTR |
| 长模式(x86-64) | IDT | 16 字节(门描述符) | 256 | IDTR |
每个 IDT 表项是一个门描述符(Gate Descriptor),包含中断处理程序的地址、特权级、段选择子等信息。CPU 收到中断向量号后,通过 IDTR 找到 IDT 基址,乘以向量号索引到对应门描述符,跳转执行。
实践提示: 在 Linux 内核中,
arch/x86/kernel/idt.c 负责初始化 IDT。网络驱动注册的中断处理函数最终会挂载到 IDT 的某个表项上。使用 cat /proc/interrupts 可以查看当前系统中每个中断号对应的 CPU 处理统计。
1.3 中断控制器:APIC 与 IOAPIC
现代 x86 系统使用 APIC(Advanced Programmable Interrupt Controller) 架构替代传统的 8259A PIC。APIC 系统由两部分组成:LAPIC(Local APIC) 和 IOAPIC(I/O APIC)。
1.3.1 LAPIC(本地 APIC)
- 集成在 CPU 内部,每个 CPU 核心拥有独立的 LAPIC。
- 管理本地中断源(如定时器、性能计数器、IPI)。
- 接收来自 IOAPIC 的中断消息,并递交给 CPU 核心。
- 支持中断优先级和中断屏蔽。
1.3.2 IOAPIC(I/O APIC)
- 位于芯片组(PCH)中,负责收集外设的中断请求。
- 将外设 IRQ 映射为中断向量,并通过系统总线发送中断消息到目标 LAPIC。
- 支持 中断重定向:可以将不同外设的中断分发到不同 CPU 核心。
- 支持 MSI(Message Signaled Interrupts):外设直接写中断消息到内存,绕过 IOAPIC。
| 特性 | 传统 8259A PIC | APIC + IOAPIC |
|---|---|---|
| 支持 CPU 数量 | 单 CPU | 多 CPU(SMP) |
| 中断优先级 | 固定 8 级 | 256 级可编程 |
| 中断分发策略 | 固定到 BSP | 可配置(轮询、固定、最低优先级) |
| MSI 支持 | 不支持 | 原生支持 |
| 可编程性 | 低 | 高(通过 IOREDTBL) |
1.4 中断处理流程与性能关键点
一个完整的中断处理流程涉及硬件和软件的紧密协作:
- 外设触发中断:网卡收到数据包后,通过 PCIe 总线发送 MSI/MSI-X 中断消息。
- IOAPIC 接收并重定向:IOAPIC 根据 IOREDTBL 配置,将中断消息发送到指定 CPU 的 LAPIC。
- LAPIC 接收并递交给 CPU:LAPIC 检查中断优先级,如果高于当前 CPU 优先级,则触发 CPU 中断。
- CPU 查找 IDT:CPU 保存当前上下文,根据中断向量号在 IDT 中找到对应的门描述符。
- 执行 ISR:CPU 跳转到 ISR 入口,执行中断处理逻辑(如网卡驱动中的
handle_irq)。 - 中断结束(EOI):ISR 执行完毕后,向 LAPIC 写入 EOI 命令,允许后续中断。
性能陷阱: 在传统中断模式下,每个数据包到达都会触发一次中断。高吞吐量场景下(如 10Gbps 网络),每秒可能产生数十万次中断,导致 CPU 频繁上下文切换,缓存污染严重。这正是 NAPI 轮询机制要解决的核心问题——在中断与轮询之间找到平衡点。
1.5 中断亲和性(IRQ Affinity)
通过设置中断亲和性,可以将特定外设的中断绑定到指定 CPU 核心上处理。这在多核系统中至关重要,可以避免中断在多个核心间迁移导致的缓存失效。
| 配置方法 | 命令/路径 | 说明 |
|---|---|---|
| 查看 IRQ 统计 | cat /proc/interrupts |
显示每个中断号在各 CPU 上的处理次数 |
| 设置亲和性 | echo 1 > /proc/irq/{IRQ_NUM}/smp_affinity |
写入 CPU 位图(十六进制),1 表示 CPU0 |
| 设置亲和性列表 | echo 0-3 > /proc/irq/{IRQ_NUM}/smp_affinity_list |
直接指定 CPU 列表 |
| 查看当前分发策略 | cat /proc/irq/{IRQ_NUM}/smp_affinity |
返回当前绑定的 CPU 位图 |
调优建议: 对于网络密集型的应用,建议将网卡中断绑定到与应用程序不同的 CPU 核心上,避免中断处理与应用线程争抢 CPU 资源。例如,将 IRQ 绑定到 CPU0-1,应用运行在 CPU2-3。使用
irqbalance 服务可以自动平衡中断分布,但在极致性能场景下建议手动绑定。