4. Linux 中断子系统基础:IRQ编号、中断描述符、中断控制器
在配置软中断CPU亲和性之前,必须理解Linux内核如何管理硬件中断。中断子系统是协议栈收包的“第一入口”,其核心组件包括IRQ编号、中断描述符和中断控制器。这三者构成了从硬件设备到内核处理函数的完整链路。
核心概念:IRQ编号是中断的“身份证”,中断描述符是中断的“档案袋”,中断控制器是中断的“交通警察”。三者协同工作,确保每个硬件中断都能被正确路由到目标CPU。
4.1 IRQ编号:中断的唯一标识
IRQ(Interrupt ReQuest)编号是Linux内核为每个中断源分配的全局唯一整数标识符。它分为两种类型:
- 硬件IRQ编号(hwirq):由中断控制器硬件定义的原始编号,与具体平台相关。
- Linux IRQ编号(irq):内核软件层使用的逻辑编号,与平台无关,用户空间通过
/proc/interrupts查看的就是此编号。
| 属性 | 硬件IRQ编号 (hwirq) | Linux IRQ编号 (irq) |
|---|---|---|
| 定义者 | 中断控制器硬件厂商 | Linux内核中断子系统 |
| 范围 | 依赖具体芯片(如0~223) | 全局线性(通常0~NR_IRQS) |
| 可见性 | 仅内核驱动可见 | 用户空间可见(procfs) |
| 映射关系 | 通过irq_domain映射到Linux IRQ | 直接用于注册中断处理函数 |
实践提示:在配置CPU亲和性时,我们操作的是Linux IRQ编号。例如,网卡队列的IRQ编号可以通过
cat /proc/interrupts | grep eth0 查看,第一列即为irq编号。
4.2 中断描述符:中断的完整档案
每个Linux IRQ编号都对应一个struct irq_desc结构体,称为中断描述符。它是内核管理中断的核心数据结构,包含了中断的所有状态和配置信息。
// 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 cpumask *irq_affinity; // CPU亲和性掩码
const char *name; // 中断名称(如 "eth0-TxRx-0")
} ____cacheline_internodealigned_in_smp;
| 字段 | 作用 | 亲和性配置相关 |
|---|---|---|
irq_affinity |
CPU亲和性掩码,决定中断发往哪些CPU | 直接修改此掩码即可改变中断路由 |
action |
链表,挂载该中断的所有处理函数 | 网卡驱动通过 request_irq() 注册 |
kstat_irqs |
每个CPU的中断计数 | 通过 /proc/interrupts 查看分布 |
handle_irq |
流处理函数,决定中断处理流程 | 通常为 handle_fasteoi_irq |
性能关键点:中断描述符中的
irq_affinity 字段是软中断CPU亲和性配置的底层操作对象。当用户写入 /proc/irq/77/smp_affinity 时,内核实际修改的就是此字段。配置不当会导致所有中断集中在CPU0,造成单核瓶颈。
4.3 中断控制器:中断路由的硬件枢纽
中断控制器(Interrupt Controller)是硬件层面的中断管理单元,负责收集设备中断信号、进行优先级仲裁,并将中断分发给指定的CPU。在ARM64和x86平台上,常见的中断控制器包括:
- GIC (Generic Interrupt Controller):ARM平台标准中断控制器,支持SPI(共享外设中断)和PPI(私有外设中断)。
- APIC (Advanced Programmable Interrupt Controller):x86平台中断控制器,包括LAPIC(本地APIC)和IOAPIC(I/O APIC)。
- MSI/MSI-X (Message Signaled Interrupts):通过PCIe总线直接发送中断消息,无需专用中断线。
| 特性 | GIC (ARM64) | APIC (x86) | MSI-X (PCIe) |
|---|---|---|---|
| 中断分发方式 | 通过GICD(分发器)路由到指定CPU | IOAPIC接收,LAPIC接收 | 直接写入目标CPU的LAPIC/MSI寄存器 |
| 亲和性配置 | GICD_ITARGETSR寄存器 | IOAPIC RTE(重定向表项) | MSI-X Table中的Target CPU字段 |
| 中断数量 | 通常支持 1024 个SPI | IOAPIC支持 24 个中断 | 每个设备最多 2048 个中断向量 |
| 性能优势 | 支持中断分组和优先级 | 支持中断重定向和负载均衡 | 每个队列独立中断,适合多队列网卡 |
多队列网卡最佳实践:现代40G/100G网卡(如Mellanox ConnectX-5、Intel XL710)使用MSI-X中断,每个硬件队列分配独立的MSI-X向量和独立的IRQ编号。这使得我们可以为每个队列的IRQ设置不同的CPU亲和性,实现1队列绑定1CPU的极致性能隔离。
4.4 中断处理流程:从硬件到软中断
理解中断的完整处理流程,有助于定位亲和性配置的生效点。一个典型的中断处理分为上半部和下半部:
- 硬件阶段:设备触发中断信号 → 中断控制器仲裁 → 发送到目标CPU。
- 上半部(硬中断):CPU保存现场 → 调用
handle_irq()→ 执行action链表中的处理函数(如网卡驱动ixgbe_msix_clean)。 - 下半部(软中断):驱动在硬中断中标记软中断(如
NET_RX_SOFTIRQ)→ 退出硬中断后,CPU执行软中断处理函数(如net_rx_action)。
亲和性生效点:中断控制器根据
irq_affinity 决定将中断发送到哪个CPU。因此,亲和性配置直接影响硬中断在哪个CPU上执行,进而影响软中断的CPU分布(软中断默认在硬中断所在的CPU上执行)。这就是为什么配置IRQ亲和性可以控制协议栈收包处理所在的CPU。
4.5 关键数据结构关系总结
将IRQ编号、中断描述符、中断控制器三者的关系总结如下:
| 组件 | 层次 | 核心作用 | 亲和性配置接口 |
|---|---|---|---|
| IRQ编号 | 软件抽象层 | 唯一标识一个中断源,用户空间操作对象 | /proc/irq/{N}/smp_affinity |
| 中断描述符 | 内核管理层 | 存储中断状态、处理函数、亲和性掩码 | irq_desc->irq_affinity |
| 中断控制器 | 硬件抽象层 | 物理路由中断信号到指定CPU | GICD_ITARGETSR / IOAPIC RTE / MSI-X Table |
性能优化启示:理解这三层结构后,我们可以得出一个关键结论——IRQ亲和性配置的本质是修改中断描述符中的CPU掩码,并通过中断控制器硬件实现物理路由。在后续章节中,我们将基于此原理,深入探讨如何为多队列网卡配置最优的亲和性策略,实现协议栈收包处理的CPU隔离和负载均衡。