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 运行在进程上下文,允许阻塞。
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() 可判断是否处于软中断上下文。这些宏在编写可重入代码时非常有用。
1.6 中断处理流程总结
- 硬件设备产生中断信号,中断控制器(如 APIC、GIC)向 CPU 发送 IRQ。
- CPU 保存当前执行上下文(寄存器、PC、栈指针),查找 IDT 获取中断处理入口。
- 执行上半部处理程序:关闭本地中断,执行最小必要操作,标记下半部。
- 退出上半部,开中断,CPU 恢复可被其他中断抢占的状态。
- 内核在合适的时机(如从系统调用返回、调度点)执行下半部处理。
- 下半部完成数据传递、协议处理等任务,最终唤醒等待的进程或完成 I/O。