2. 上半部与下半部:上半部(硬中断)的紧急任务、下半部的延迟处理需求、为什么需要下半部机制

2.1 上半部(硬中断)的紧急任务

当硬件设备产生中断信号时,CPU 会立即暂停当前执行的进程,跳转到中断服务例程(ISR,Interrupt Service Routine)执行。这个阶段被称为上半部(Top Half),其核心特征是原子性与紧迫性。

上半部必须完成以下不可延迟的任务:

  • 保存硬件状态:读取设备的状态寄存器,确认中断源,防止中断丢失。
  • 清除中断标志:向设备发送 ACK 信号,解除硬件中断请求线,允许后续中断到达。
  • 拷贝关键数据:从硬件 FIFO 或 DMA 缓冲区中快速搬移少量时间敏感数据(如网卡接收到的第一个数据包描述符)。
  • 标记中断发生:设置一个软件标志位或唤醒一个内核线程,通知下半部有工作待处理。
关键约束:上半部执行期间,当前 CPU 上的中断是关闭的(或至少当前中断线被屏蔽)。这意味着上半部代码必须短小精悍,通常只占用几十到几百微秒。任何耗时操作(如复杂的协议解析、磁盘 I/O、内存分配)都严禁出现在上半部中。

2.2 下半部的延迟处理需求

与上半部的“立即响应”相反,下半部(Bottom Half)负责处理那些可以推迟但必须最终完成的工作。下半部在中断使能的环境下执行,允许被更高优先级的中断打断。

典型的下半部处理内容包括:

处理类别 具体任务 延迟容忍度
数据协议处理 TCP/IP 协议栈解析、重组、校验和计算 毫秒级
驱动后处理 更新软件统计计数器、管理环形缓冲区指针 毫秒级
唤醒等待进程 唤醒因等待 I/O 而睡眠的用户进程 微秒~毫秒级
内存管理操作 释放 skb(socket buffer)、调整内存池水位 毫秒级

下半部的设计哲学是:将非紧急但繁重的计算任务从中断上下文中剥离出来,放到一个更宽松的执行环境中运行,从而大幅降低系统关中断时间,提升实时性和吞吐量。

2.3 为什么需要下半部机制

如果没有下半部机制,所有中断处理都必须在关中断的上半部中完成,系统将面临以下严重问题:

  1. 中断延迟不可控:长时间关中断会导致高优先级中断(如时钟中断)被阻塞,系统时间精度下降,甚至丢失关键事件。
  2. CPU 利用率低下:上半部不能睡眠、不能调度,只能忙等或轮询,浪费大量 CPU 周期。
  3. 嵌套中断风险:若在上半部中开启中断以允许嵌套,则复杂的嵌套逻辑极易导致栈溢出或竞态条件。
  4. 缺乏优先级管理:所有上半部任务优先级相同,无法区分“紧急硬件响应”和“普通数据处理”。

下半部机制通过将中断处理拆分为两个阶段,完美解决了上述矛盾:

核心收益:
  • 上半部快速关闭中断 → 降低硬件响应延迟。
  • 下半部在开中断环境下执行 → 允许中断嵌套,保证高优先级事件及时响应。
  • 下半部可被调度、可睡眠(如工作队列) → 支持复杂操作,避免 CPU 空转。
  • 提供多种下半部实现(tasklet、软中断、工作队列) → 按需选择实时性与吞吐量的平衡点。

2.4 上半部与下半部的协作流程

下图展示了从硬件中断触发到下半部执行的完整生命周期:

上半部与下半部协作流程 上半部与下半部协作流程 硬件中断触发 上半部(硬中断上下文) 关中断 · 保存状态 · 清除标志 标记下半部待处理 下半部(开中断上下文) 协议处理 · 唤醒进程 · 释放内存 上半部特点 • 原子执行,不可睡眠 • 关中断,必须极快 • 只做最必要的硬件操作 下半部特点 • 开中断,可被抢占 • 可延迟执行(软中断/tasklet) • 可睡眠(工作队列)

2.5 上半部 vs 下半部:关键差异对比

对比维度 上半部(硬中断) 下半部(软中断/tasklet/工作队列)
执行上下文 中断上下文(不可睡眠) 软中断上下文 / 进程上下文(可睡眠)
中断状态 关中断(当前 CPU) 开中断(允许嵌套)
执行时机 硬件中断触发后立即执行 上半部退出后,由内核调度执行
典型耗时 微秒级(< 100 µs) 毫秒级(可长达数毫秒)
可延迟性 不可延迟,必须立即响应 可延迟,由内核决定执行时机
实现机制 注册 IRQ handler tasklet / softirq / workqueue

2.6 下半部机制的演进与选择

Linux 内核提供了多种下半部实现机制,开发者可根据场景选择:

  • 软中断(SoftIRQ):静态分配,性能最高,用于网络 RX/TX、定时器等高频路径。
  • tasklet:基于软中断的动态实现,同一 tasklet 串行执行,适合驱动开发者使用。
  • 工作队列(Workqueue):运行在进程上下文,可睡眠,适合需要持有锁或执行文件系统操作的任务。