内核中断处理 —— 从硬件信号到上半部/下半部的完整剖析
CPU 正跑着你的代码,网卡收到一个包、磁盘写完一次、时钟每毫秒滴答一次——这些异步事件都靠中断(interrupt) 打断 CPU、让内核立刻处理。中断是操作系统响应外部世界的唯一异步入口,它的处理效率直接影响系统吞吐和延迟。本篇是导读页:讲中断的整体认知骨架——怎么分类、内核为什么这么设计、处理流程长什么样、上半部/下半部怎么分工,以及观测手段与跨文档关系。完整细节拆在四个独立子篇里。
本篇导航(太长拆成了四篇,各自独立成章;「分类体系」含原「设计考量」,「下半部机制」含原「下半部内部实现」,均已合并):
| 拆分篇 | 内容 |
|---|---|
| 中断分类体系与设计考量 | 五类中断完整谱系(外部中断 / 异常 / 软中断陷入 / NMI / IPI)+ 五大设计矛盾与权衡 |
| 处理流程 | 从 IRQ 信号到 handler 返回的每一步:IST 栈切换、运行时限制、返回触发调度、IDT |
| 上半部 / 下半部机制与内部实现 | softirq / tasklet / workqueue 机制与内部交接(位图 / 链表 / 唤醒内核线程)+ NAPI + 一次完整收包 |
| 开销量化 | 四部分开销构成、量化数字、观测手段 |
相关:系统调用(同为进内核,主动同步陷入 vs 中断被动异步打断)见 syscall;中断上下文的运行时限制见 处理流程、不能睡眠的根源见 分类体系与设计考量;中断上下文切换的概览见 context-switch §4.3;中断处理后唤醒进程 → 调度见 scheduling;中断亲和性(IRQ affinity)见 irq-affinity;线程绑核见 thread-affinity;本文涉及的硬件寄存器(RFLAGS.IF/APIC/IDT 等)速查见 x86-64-registers;
%irq/%soft观测见 mpstat;中断计数见 procfs;DMA 完成后靠中断通知 CPU 见 dma;中断控制器的进化(8259A→APIC→xAPIC→x2APIC→MSI-X)见 8259 §七和 irq-affinity §二;网卡收包完整流程见 上半部 / 下半部机制与内部实现 末章。
零、一句话认知:中断 = 硬件异步打断 CPU,内核放下手头活去处理
正常情况下 CPU 一条条执行指令。当外设(网卡/磁盘/键盘/时钟)需要 CPU 注意时,它通过中断控制器(APIC)给 CPU 发一个中断请求(IRQ)。CPU 在当前指令边界停下,保存现场,跳到内核预先登记的中断处理程序(ISR / interrupt handler),处理完恢复现场、回到原来被打断的地方继续跑——被打断的进程通常毫不知情。
核心记忆:中断是硬件发起的、异步的打断,不是进程主动的。它和系统调用都让 CPU 进内核态,但系统调用是进程"我主动要服务"(同步),中断是外设"我有事,你必须停下"(异步)。二者共用"存现场→进内核→干活→恢复现场"的骨架,但触发方完全不同。
分类速览:五类"中断"
"中断"是广义统称,从触发源和性质分五类(完整展开见 中断分类体系):
| 类别 | 谁触发 | 同步/异步 | 可屏蔽? | 典型例子 |
|---|---|---|---|---|
| 外部中断(IRQ) | 外设 → APIC | 异步 | 可(IF 标志位) | 网卡收包、磁盘 DMA 完成、时钟 tick |
| 异常(Exception) | 当前指令自己出错 | 同步 | 不可 | 缺页、除零、非法指令 |
| 软中断陷入(Trap) | 程序主动发起 | 同步 | 不可 | int 0x80 / syscall(系统调用入口) |
| NMI | 硬件紧急事件 | 异步 | 不可 | 硬件故障、watchdog、perf PMI |
| IPI | 其他 CPU 核 | 异步 | 可 | TLB shootdown、触发重新调度 |
容易混的一对:软中断陷入(trap,系统调用的入口机制)和下半部的**软中断(softirq)**中文同名但完全是两码事,详见 分类体系。
处理流程速览:一次中断的 14 步
从硬件信号到处理完毕,x86-64 上的完整链路(逐步拆解与 IST 栈切换/运行时限制/调度触发见 处理流程):
外设 → APIC → CPU 检测 INTR → 硬件压栈(SS/RSP/RFLAGS/CS/RIP)
→ 查 IDT 找 handler → 切 IST 独立中断栈 → 汇编入口 SAVE_ALL
→ do_IRQ() 应答 EOI → 上半部(快接快应答,关中断窗口)
→ 检查 pending softirq → 下半部(开中断,干重活)
→ RESTORE_ALL → iret 返回前查 TIF_NEED_RESCHED → 可能触发进程调度三个最该记住的要点:
- IST 独立中断栈:中断在 per-CPU 的 16KB 中断栈上跑,与被打断进程的内核栈彻底隔离。
- 中断上下文没有 task_struct:不能睡眠、不能调度、不能碰用户态地址;唯一的调度触发点在
iret返回前的need_resched检查。 - 中断不换页表:内核空间所有进程共享,所以中断可以"借"被打断进程的地址空间干活——这也是它快的原因之一。
上半部 / 下半部速览:急活重活拆开干
中断 handler 期间同类中断被屏蔽,所以 handler 必须极短;但活不少(比如走完网络协议栈)。内核把处理劈成两半(机制与内部实现均见 上半部 / 下半部机制与内部实现):
| 上半部(硬中断 handler) | 下半部 | |
|---|---|---|
| 环境 | 关着中断,响应不了新中断 | 开着中断,可被打断 |
| 干什么 | 应答设备、搬数据、登记待办 | 走协议栈、唤醒进程等重活 |
| 时长要求 | 越短越好(< 几微秒) | 可以长一些 |
| 机制 | — | softirq(不能睡眠,最快)/ tasklet(不并行)/ workqueue(能睡眠) |
网卡就是典型:上半部只把包搬进内存队列并应答,下半部(NET_RX 软中断)才走协议栈。高负载下 NAPI 会关掉中断改轮询批量收包,避免中断风暴。
多核与中断亲和性
- 中断默认落在某个核:每个 IRQ 由某个(或某组)CPU 核处理。默认可能全压在 CPU0,导致 mpstat 里 CPU0 的
%soft/%irq爆高、别的核闲着。 - 中断亲和性
/proc/irq/<n>/smp_affinity:用位图指定某个 IRQ 允许被哪些核处理,把它从关键业务核挪走,别让软中断抢关键线程的 CPU(详见 irq-affinity,含 IO-APIC/MSI-X 硬件机制、irqbalance 原理、网卡多队列实践)。 - 网卡多队列(RSS)/RPS/RFS:现代网卡有多个硬件队列,可把收包中断分散到多个核并行处理;软件层的 RPS/RFS 则在软中断阶段把包分给不同核。这是高吞吐网络消除"单核软中断瓶颈"的标准手段。
- IPI(处理器间中断):一个核要通知别的核做事(如 TLB shootdown——改了页表要让别的核刷 TLB,见 tlb;或触发别的核重新调度)就发 IPI。
观测命令速查
cat /proc/interrupts # 每个 IRQ 在每个 CPU 上的累计次数(看中断压在哪个核)
cat /proc/softirqs # 每种软中断(NET_RX/NET_TX/TIMER/SCHED...)在每核的次数
mpstat -P ALL 1 # 逐核 %irq(硬中断)/%soft(软中断)占比 —— 看是否集中单核
cat /proc/irq/<n>/smp_affinity # 查某 IRQ 的亲和性掩码(hex 位图)
echo 4 > /proc/irq/<n>/smp_affinity # 把 IRQ n 绑到 CPU2(bit2=1),需 root
top # ksoftirqd/<n> 高 或 %si 高 → 软中断繁忙(多为网络)
vmstat 1 # 'in' 列 = 每秒中断数,'cs' = 上下文切换%irq高(硬中断):设备中断频繁,可能中断风暴、驱动问题。%soft高(软中断):多为网络收发繁忙,cat /proc/softirqs看NET_RX/NET_TX增长,考虑网卡多队列/RPS 分散。ksoftirqd吃 CPU:软中断多到当场处理不完,被推给内核线程消化——网络风暴的典型信号。
和本仓库其他文档的关系
- 对照:系统调用:syscall——同样进内核态,但系统调用是进程主动同步陷入,中断是外设异步打断;老式
int 0x80正是"软中断陷入"(见 分类体系)。 - 中断之后:scheduling——下半部唤醒进程 → 进运行队列 → 等调度;IPI 可触发重新调度。
- 中断上下文切换:context-switch §4.3——'借壳执行'的关键特征总览与引用;详细分析见 分类体系与设计考量(不能睡眠)、处理流程(IST 栈切换、运行时限制、中断返回与调度)。
- 中断亲和/多核:irq-affinity(IRQ affinity 完整论述:硬件机制/smp_affinity/irqbalance/RSS 配合)、thread-affinity(线程绑核,与中断绑核互为补充)、mpstat(
%irq/%soft逐核)。 - 异常也是中断:mmap/tlb(缺页异常走 IDT)、signals(SIGSEGV 源于缺页异常无法满足)。
- DMA 靠中断通知:dma——DMA 传输完成后通过中断通知 CPU。
- 观测数据源:procfs(
/proc/interrupts、/proc/softirqs)、vmstat(in中断数)。 - perf 用 NMI:perf-internals(PMU 采样中断 PMI 走 NMI)。
总结
中断是硬件异步打断 CPU、逼内核放下手头活去处理外设事件(网卡/磁盘/时钟)的机制。从分类上分为五类:外部中断(IRQ,异步可屏蔽)、异常(指令出错,同步不可屏蔽)、软中断陷入(系统调用入口,同步主动)、NMI(硬件紧急,不可屏蔽)、IPI(核间通信,异步)。内核面临五大设计矛盾——响应延迟 vs 处理工作量、单核集中 vs 多核分散、关中断保护临界区、中断上下文不能睡眠、中断风暴——分别通过上半部/下半部拆分、中断亲和性与多队列、spin_lock_irqsave、workqueue 进程上下文、NAPI 中断+轮询来解决。完整流程从硬件信号经 APIC→IDT→TSS.IST 栈切换→汇编入口→do_IRQ→上半部→下半部→iret 返回;IST 确保中断在 per-CPU 独立栈上运行,与 task 内核栈完全隔离;中断上下文因无 task_struct 而不可睡眠/不可调度,唯一的调度触发点在 iret 返回前检查 TIF_NEED_RESCHED。中断开销来自四部分:硬件响应、上下文保存恢复、缓存污染、TLB 冲刷,其中缓存污染最难量化但影响最大。