1. 中断上下文与进程上下文
在Linux内核驱动开发中,理解“上下文”是编写正确、高效延迟工作代码的基石。中断上下文与进程上下文是内核中两种截然不同的执行环境,它们对可执行的操作、可调用的函数以及代码的延迟特性有着严格的限制。混淆这两种上下文是驱动开发中最常见的错误来源之一。
核心要点: 中断上下文是异步、不可阻塞的“快速通道”;进程上下文是可阻塞、可调度的“慢速通道”。延迟工作的选择(tasklet vs workqueue)本质上就是在这两种通道之间做权衡。
1.1 两种上下文的本质区别
下表从多个关键维度对比了中断上下文与进程上下文的核心差异:
| 对比维度 | 中断上下文 | 进程上下文 |
|---|---|---|
| 触发方式 | 硬件中断信号(异步) | 系统调用、异常、内核线程(同步/异步) |
| 关联进程 | 无(不隶属于任何用户进程) | 有(current 宏有效,可访问进程描述符) |
| 可睡眠/阻塞 | 绝对禁止 | 允许(可调用 schedule()、等待队列等) |
| 可调用的内核API | 仅限 GFP_ATOMIC 内存分配、spinlock、硬件寄存器操作 |
几乎全部内核API(包括 GFP_KERNEL、mutex、copy_from_user) |
| 抢占性 | 不能被其他进程抢占(但可被更高优先级中断嵌套) | 可被调度器抢占 |
| 执行时间要求 | 极短(微秒级),必须快速完成 | 相对宽松(毫秒级甚至更长) |
| 典型场景 | 硬件事件响应、数据接收、清除中断标志 | 文件操作、网络协议栈、复杂数据处理 |
1.2 中断上下文:内核的“紧急响应通道”
当硬件设备(如网卡、磁盘控制器)需要内核立即关注时,会触发一个中断信号。CPU 会立即暂停当前正在执行的进程,保存其上下文,并跳转到预先注册的中断处理程序(Interrupt Service Routine, ISR)。此时,内核便进入了中断上下文。
中断上下文的核心约束是不可阻塞。因为中断处理程序不隶属于任何进程,没有进程控制块(PCB)可供调度器挂起和恢复。如果在中断上下文中尝试睡眠(例如调用 msleep() 或获取一个已被占用的 mutex),内核将直接崩溃(Kernel Panic)。
开发避坑: 在中断上下文中,绝对不要使用
kmalloc(GFP_KERNEL)。必须使用 kmalloc(GFP_ATOMIC),因为 GFP_KERNEL 分配可能触发内存回收而睡眠。同样,copy_from_user() 和 copy_to_user() 也可能因缺页中断而睡眠,在中断上下文中调用它们会导致死锁或崩溃。
下图展示了中断上下文的生命周期及其与进程上下文的关系:
1.3 进程上下文:内核的“常规工作通道”
进程上下文是内核执行系统调用、内核线程或异常处理时的环境。此时,内核代表一个特定的进程运行,全局变量 current 指向该进程的 task_struct。进程上下文拥有完整的进程控制块,因此可以睡眠、被抢占、参与调度。
在进程上下文中,驱动开发者可以使用绝大多数内核API,包括:
- 内存分配:
kmalloc(GFP_KERNEL)、vmalloc() - 同步机制:
mutex_lock()、down_interruptible() - 用户空间访问:
copy_from_user()、copy_to_user() - 调度相关:
schedule()、wait_event()
下图展示了进程上下文在系统调用中的典型路径:
1.4 上下文对延迟工作的限制
由于中断上下文对执行时间和可调用API的严格限制,驱动开发中通常采用“上半部/下半部”(Top Half / Bottom Half)机制:
- 上半部(Top Half): 在中断上下文中执行,只做最紧急、最快速的工作(如读取硬件状态寄存器、清除中断标志位)。
- 下半部(Bottom Half): 将耗时的数据处理工作推迟到更宽松的上下文中执行。Linux 提供了多种下半部机制,其中 tasklet 和 workqueue 是最常用的两种。
选择哪种下半部机制,直接取决于你希望它在哪种上下文中运行:
| 下半部机制 | 执行上下文 | 可睡眠 | 适用场景 |
|---|---|---|---|
| Tasklet | 中断上下文(软中断) | ❌ 禁止 | 对时间要求极高、不可阻塞的快速处理(如网络数据包接收) |
| Workqueue | 进程上下文(内核线程) | ✅ 允许 | 需要睡眠、需要大量内存分配、需要访问用户空间的复杂操作 |
选择指南: 如果你不确定该用哪个,先问自己一个问题:“我的延迟工作是否需要睡眠?” 如果答案是“是”,请使用 workqueue;如果答案是“否”,且执行时间极短(微秒级),tasklet 是更轻量、更高效的选择。在后续章节中,我们将深入探讨这两种机制的具体实现与性能权衡。