网卡收到包、磁盘完成 I/O、键盘按键——都通过中断通知 CPU。中断是 OS 与硬件对话的核心通道。
一、先看图
flowchart TD
DEV[硬件设备] -->|MSI-X| APIC[Local APIC<br/>per-CPU]
DEV -->|legacy INTx| IOAPIC[I/O APIC]
IOAPIC --> APIC
APIC --> IDT[IDT<br/>中断描述符表]
IDT --> HANDLER[irq_desc → handler<br/>handle_irq]
HANDLER --> HARDIRQ[hardirq 处理<br/>最小化工作]
HARDIRQ --> SOFTIRQ[softirq / threaded<br/>延迟处理]
classDef hw fill:#388bfd22,stroke:#388bfd,color:#adbac7;
classDef sw fill:#3fb95022,stroke:#3fb950,color:#adbac7;
class DEV,APIC,IOAPIC hw
class IDT,HANDLER,HARDIRQ,SOFTIRQ sw
二、x86 中断路径
2.1 IDT(Interrupt Descriptor Table)
256 个条目:
- 0-31:CPU 异常(page fault、divide error…)
- 32-255:外部中断 + IPI + 软件中断
2.2 Local APIC
每 CPU 一个 Local APIC:
- 接收中断
- 发送 IPI(Inter-Processor Interrupt)
- 管理优先级
2.3 I/O APIC
外部设备的 legacy 中断 → I/O APIC → 路由到目标 CPU 的 Local APIC。
三、MSI/MSI-X
现代设备(NVMe、网卡)使用 MSI/MSI-X:
- MSI:设备向 CPU 写一个内存地址(MMIO)→ 触发中断
- MSI-X:每 queue 独立 MSI → 多队列 I/O → 每 CPU 独立中断
优势:
- 不需要 I/O APIC 路由
- 不共享中断线
- 低延迟
四、irq_desc
struct irq_desc {
struct irq_data irq_data;
irq_flow_handler_t handle_irq; // 控制器级 handler
struct irqaction *action; // 设备驱动 handler 链
const char *name;
// ...
};每个 IRQ 号对应一个 irq_desc →
串联设备驱动注册的 handler。
五、中断处理流程
- 硬件触发 → APIC → CPU 跳转到 IDT entry
- 保存寄存器 → 调用
do_IRQ()/handle_domain_irq() - 查找
irq_desc→ 调用handle_irq()(如handle_fasteoi_irq) - 遍历
action链 → 调用每个设备 handler - handler 返回
IRQ_HANDLED/IRQ_WAKE_THREAD - ACK 中断 → 恢复
六、IRQ affinity
# 查看 IRQ 分布
cat /proc/interrupts
# 设置 IRQ 23 只在 CPU 0-3 上处理
echo 0f > /proc/irq/23/smp_affinity
# irqbalance 自动平衡
systemctl status irqbalanceNVMe 多队列:每个 queue 绑定到不同 CPU → 避免跨核中断。
七、IPI
CPU 间通信用 IPI:
- TLB shootdown
- 调度通知(
resched_curr) - 函数调用(
smp_call_function)
IPI 是最高优先级中断。
八、中断风暴
设备持续产生中断 → CPU 100% 在 hardirq 中 → 应用无法运行。
缓解:
- NAPI(网络):高频时切换到 polling
- coalesce:NVMe/网卡延迟合并中断
- threaded IRQ:避免长时间占用 hardirq 上下文
九、ARM 中断
ARM 用 GIC(Generic Interrupt Controller):
- GIC-400 / GIC-500 / GIC-700
- SGI(Software Generated Interrupt)= IPI
- PPI(Private Peripheral Interrupt)= per-CPU
- SPI(Shared Peripheral Interrupt)= 共享
设备树描述中断拓扑。
十、小结
- 中断路径:设备 → APIC/GIC → IDT/异常表 → irq_desc → handler
- MSI-X 实现多队列独立中断
- IRQ affinity 绑定避免跨核开销
- 中断风暴需要 NAPI/coalesce/threaded IRQ 缓解
参考文献
kernel/irq/arch/x86/kernel/irq.cDocumentation/IRQ.txt- Intel SDM, “APIC Architecture.”
工具
/proc/interrupts/proc/irq/<n>/smp_affinityirqbalanceperf stat -e irq:*bpftrace -e 'tracepoint:irq:irq_handler_entry { @[args->name] = count(); }'
上一篇:形式化验证 下一篇:softirq/tasklet/workqueue
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】内存回收
Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。
【操作系统百科】交换
swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。
【操作系统百科】Slab/SLUB 分配器
buddy 只管页粒度(4K+),内核大多数对象只有几十到几百字节。slab/SLUB 在 buddy 之上做对象级缓存。本文讲 slab 历史、SLUB 接手、SLOB 退场、kmem_cache、per-CPU cache、KASAN 集成。
【操作系统百科】用户态分配器
glibc malloc、tcmalloc、jemalloc、mimalloc 各有哲学。本文讲 arena、thread cache、size class、madvise 返还策略、碎片与 RSS 膨胀、如何根据负载选分配器。