1. 中断基础概念:中断的定义、中断向量、中断上下文

1.1 中断的定义

中断(Interrupt)是硬件或软件向处理器发送的一种异步信号,用于通知处理器发生了需要立即关注的事件。中断机制是现代操作系统实现多任务、响应外部设备的核心基石。当中断发生时,CPU 会暂停当前正在执行的指令流,保存现场,跳转到预先注册的中断处理程序执行,处理完成后再恢复原来的执行流。

核心特征:中断是异步的、优先级驱动的、由事件触发的。与轮询(Polling)相比,中断能显著降低 CPU 的空转等待开销,提升系统整体吞吐量。

1.2 中断向量与中断号

中断向量(Interrupt Vector)是中断处理程序入口地址的索引表。每个中断源被分配一个唯一的中断号(IRQ Number),CPU 根据中断号在中断向量表中查找对应的处理函数地址。在 x86 架构中,IDT(Interrupt Descriptor Table)最多支持 256 个中断向量;在 ARM 架构中,GIC(Generic Interrupt Controller)管理中断路由与优先级。

中断类型 中断号范围 典型用途
异常(Exception) 0–31(x86) 除零、缺页、非法指令
硬件中断(IRQ) 32–255(x86) 网卡、磁盘、定时器
软件中断(IPI) 由架构定义 核间通信、TLB 刷新
提示:在 Linux 中,/proc/interrupts 文件列出了当前系统中所有注册的中断号、CPU 亲和性以及累计触发次数,是排查中断均衡问题的第一入口。

1.3 中断上下文:上半部与下半部

中断上下文(Interrupt Context)是指 CPU 在执行中断处理程序时所处的特殊运行环境。Linux 内核将中断处理拆分为两个阶段:上半部(Top Half) 和 下半部(Bottom Half),以平衡实时性与处理吞吐量。

  • 上半部(硬中断处理程序):在关闭本地中断或屏蔽当前中断线的条件下执行,要求极短且不可睡眠。典型操作:清除中断标志、拷贝硬件寄存器数据、触发下半部。
  • 下半部(软中断 / Tasklet / Workqueue):在开中断环境下执行,允许延迟处理、可睡眠(Workqueue),适合执行数据协议解析、网络包分发等耗时操作。
关键约束:上半部中禁止调用可能导致睡眠的函数(如 kmalloc(GFP_KERNEL)、mutex_lock()),否则会触发内核 BUG。下半部中 Tasklet 仍处于软中断上下文,同样不可睡眠;只有 Workqueue 运行在进程上下文,允许阻塞。
中断上下文分层与执行流 硬件中断事件 上半部(Top Half) 关中断 · 极短 · 不可睡眠 · 清除标志 软中断(Softirq) 开中断 · 不可睡眠 Tasklet 基于 Softirq · 串行化 Workqueue 进程上下文 · 可睡眠 恢复被中断的执行流

1.4 上半部与下半部的对比

对比维度 上半部(硬中断) 下半部(软中断 / Tasklet / Workqueue)
执行上下文 中断上下文(不可睡眠) 软中断上下文 / 进程上下文
中断状态 通常关闭本地中断 开中断,可被更高优先级中断抢占
执行时长 微秒级,必须极短 毫秒级,允许较长处理
可调用函数 仅限 GFP_ATOMIC、spinlock 允许 GFP_KERNEL、mutex(Workqueue)
典型任务 ACK 中断、拷贝 FIFO 数据 协议栈处理、网络包分发、I/O 完成回调

1.5 中断上下文与进程上下文的区别

理解中断上下文与进程上下文的差异,是编写正确驱动和优化中断性能的前提。中断上下文没有进程控制块(task_struct)的关联,不能访问用户空间,也不能触发页面调度。

  • 进程上下文:有 current 指针,可睡眠,可访问用户态地址,持有 mm_struct。
  • 中断上下文:无 current 指针(或指向被中断进程),不可睡眠,不可访问用户态,内核栈独立且较小(通常 4KB–16KB)。
调试技巧:在中断处理程序中调用 in_interrupt() 可判断当前是否处于中断上下文;in_softirq() 可判断是否处于软中断上下文。这些宏在编写可重入代码时非常有用。
中断上下文 vs 进程上下文 中断上下文 • 无 current 指针 • 不可睡眠 • 不可访问用户空间 • 内核栈 4KB–16KB • 使用 spinlock 同步 • 禁止触发缺页 进程上下文 • 有 current 指针 • 可睡眠(schedule()) • 可访问用户空间 • 内核栈 8KB+ • 使用 mutex 同步 • 允许缺页与换入 界限:不可跨越

1.6 中断处理流程总结

  1. 硬件设备产生中断信号,中断控制器(如 APIC、GIC)向 CPU 发送 IRQ。
  2. CPU 保存当前执行上下文(寄存器、PC、栈指针),查找 IDT 获取中断处理入口。
  3. 执行上半部处理程序:关闭本地中断,执行最小必要操作,标记下半部。
  4. 退出上半部,开中断,CPU 恢复可被其他中断抢占的状态。
  5. 内核在合适的时机(如从系统调用返回、调度点)执行下半部处理。
  6. 下半部完成数据传递、协议处理等任务,最终唤醒等待的进程或完成 I/O。