3. 中断处理机制:中断类型、中断向量表、IDT 与处理流程

在 CPU 绑定场景下,中断处理是影响调度延迟最核心的硬件/软件协同机制。中断处理不当会导致目标 CPU 被频繁打断,破坏缓存亲和性,并引入不可预测的调度抖动。本节从中断类型、向量表结构、描述符表布局以及上下半部处理流程四个维度展开分析。

3.1 中断类型:硬中断与软中断

中断按触发源和同步性分为两大类:硬中断(Hard IRQ) 和 软中断(SoftIRQ)。两者的核心差异在于触发源、执行上下文和可延迟性。

对比维度 硬中断(Hard IRQ) 软中断(SoftIRQ)
触发源 硬件设备(网卡、磁盘、定时器) 软件主动触发(内核代码调用 raise_softirq())
执行上下文 中断上下文(不可睡眠、不可调度) 软中断上下文(可被硬中断抢占,不可睡眠)
优先级 高(立即响应,抢占当前进程) 低(在硬中断返回后或 ksoftirqd 中执行)
可延迟性 不可延迟,必须尽快处理 可延迟,允许累积批量处理
典型用途 数据拷贝、寄存器保存、中断应答 网络协议栈处理、定时器回调、任务调度
CPU 绑定影响 直接打断目标 CPU 当前任务 在目标 CPU 上排队执行,影响调度延迟
CPU 绑定场景提示: 硬中断亲和性(IRQ Affinity)决定了哪个 CPU 处理设备中断。若将网卡中断绑定到业务 CPU,每次网络包到达都会打断业务线程,导致调度延迟急剧上升。实践中应将中断分散到非业务 CPU 或专用中断处理核。

3.2 中断向量表与中断描述符表(IDT)

中断向量表是 CPU 硬件与操作系统之间的桥梁。x86 架构使用 中断描述符表(IDT) 替代了早期的向量表,每个中断源对应一个 8 字节或 16 字节的描述符项。

3.2.1 中断向量分配

中断向量号(0–255)的分配遵循固定约定:

  • 0–31:异常和陷阱(除零、缺页、保护错误等),由 CPU 硬件保留。
  • 32–47:可屏蔽硬中断(IRQ 0–15),对应传统 PIC 或 IOAPIC 输入。
  • 48–255:系统调用(如 Linux 使用 0x80)、自定义中断、IPI(核间中断)。

3.2.2 IDT 描述符结构

每个 IDT 项包含以下关键字段:

字段 位宽 说明
Offset[15:0] 16 bit 中断处理函数地址低 16 位
Segment Selector 16 bit 代码段选择子(通常指向内核代码段)
IST 3 bit 中断栈表索引(用于切换栈)
Type 4 bit 门类型:中断门、陷阱门、任务门
DPL 2 bit 描述符特权级(0 为内核态,3 为用户态)
Present 1 bit 描述符是否有效
Offset[31:16] 16 bit 中断处理函数地址高 16 位
关键点: 中断门与陷阱门的区别在于中断门会清除 IF 标志(禁止嵌套中断),陷阱门则保留 IF。在 CPU 绑定场景下,使用中断门可以防止同一 CPU 被同类型中断嵌套打断,降低调度延迟抖动。

3.2.3 IDT 初始化与加载

Linux 内核在启动时通过 setup_idt() 初始化 IDT,并使用 lidt 指令加载 IDTR 寄存器。以下为简化示例:

/* 设置中断门:向量号 0x20(IRQ 0) */
static void set_intr_gate(unsigned int n, void *addr)
{
    struct idt_desc *desc = &idt_table[n];
    desc->offset_low  = (unsigned long)addr & 0xFFFF;
    desc->segment     = __KERNEL_CS;
    desc->ist         = 0;
    desc->type        = 0x0E;  /* 32 位中断门 */
    desc->dpl         = 0;
    desc->present     = 1;
    desc->offset_high = ((unsigned long)addr >> 16) & 0xFFFF;
}

/* 加载 IDT */
asm volatile("lidt %0" : : "m" (idt_descriptor));

3.3 中断处理流程:上半部与下半部

现代操作系统将中断处理拆分为两个阶段,以平衡响应速度和系统吞吐量。

3.3.1 上半部(Top Half)

上半部在硬中断上下文中执行,要求快速、原子、不可阻塞。典型操作包括:

  • 保存 CPU 寄存器状态
  • 读取设备状态寄存器,确认中断源
  • 清除设备中断标志
  • 将数据从设备 FIFO 拷贝到内存缓冲区
  • 标记下半部需要执行(触发软中断)
  • 发送 EOI(End of Interrupt)到中断控制器

3.3.2 下半部(Bottom Half)

下半部在软中断或任务上下文中执行,允许延迟、批量处理。Linux 提供三种下半部机制:

机制 执行时机 可睡眠 适用场景
SoftIRQ 硬中断返回后立即检查 否 网络 RX/TX、定时器、调度器
Tasklet 基于 SoftIRQ 封装 否 驱动下半部(旧接口)
Workqueue 内核工作线程上下文 是 需要睡眠的复杂处理(文件系统、块设备)
CPU 绑定场景风险: 若下半部使用 Workqueue 且绑定到业务 CPU,当工作线程被唤醒时可能抢占业务线程,导致调度延迟。建议将 Workqueue 绑定到非业务 CPU 或使用 wq_unbound 标志。

3.4 完整中断处理流程 SVG 架构图

下图展示了从硬件中断触发到下半部完成的完整数据流,重点标注了 CPU 绑定场景下的关键决策点。

中断处理流程:上半部与下半部 中断处理完整流程(上半部 / 下半部) 硬件设备(网卡/磁盘) 中断控制器(IOAPIC) IDT 查找中断处理函数 上半部(Top Half) 保存寄存器 读取设备状态 发送 EOI 下半部(Bottom Half) SoftIRQ / Tasklet Workqueue 网络协议栈 / 定时器 文件系统 / 块设备 不可睡眠 可睡眠 CPU 绑定决策点 中断亲和性 → 目标 CPU

3.5 中断处理对调度延迟的影响分析

在 CPU 绑定场景下,中断处理对调度延迟的影响体现在以下三个层面:

  1. 硬中断抢占:当业务线程正在执行时,硬中断到达绑定 CPU,CPU 立即保存现场并跳转到中断处理函数。从业务线程角度看,这段时间属于“不可运行”的调度延迟。
  2. 下半部排队:软中断或 Workqueue 在目标 CPU 上排队执行,若下半部处理时间过长,会延迟业务线程的恢复执行。
  3. 缓存污染:中断处理函数访问的设备数据结构和协议栈代码会污染 CPU 缓存,业务线程恢复后需要重新加载缓存,增加执行时间。
优化策略:
  • 使用 /proc/irq/<IRQ>/smp_affinity 将中断绑定到非业务 CPU。
  • 启用 threaded IRQ 将硬中断处理线程化,允许调度器管理中断线程优先级。
  • 对延迟敏感的业务线程使用 SCHED_FIFO 实时优先级,确保中断线程不会无限抢占。

3.6 本章小结

中断处理机制是 CPU 绑定场景下调度延迟的核心来源。硬中断提供快速响应但不可延迟,软中断提供批量处理但增加排队延迟。IDT 作为硬件与软件的接口,决定了中断处理函数的入口。理解上下半部的拆分逻辑,结合中断亲和性配置,是优化调度延迟的基础。