2. Linux中断子系统基础:中断向量表、中断描述符、中断处理流程(上半部与下半部)

中断是操作系统响应硬件事件的异步机制,是驱动延迟分析的核心对象。本章从硬件向量表到软件处理流程,建立中断子系统的完整知识框架。

2.1 中断向量表(IDT)

在x86架构中,中断向量表(Interrupt Descriptor Table, IDT)是CPU用于查找中断处理入口的256项结构。每个中断或异常对应一个0–255的向量号。

向量范围 类型 典型用途
0–31 异常与NMI 除零、缺页、调试、机器检查等
32–47 IRQ(硬件中断) 定时器、键盘、磁盘、网卡等
48–255 软件中断与自定义 系统调用(0x80)、IPI、分配向量
关键点:向量号0–31由Intel/AMD固定,32–47对应传统PIC的IRQ0–IRQ15,48以上由内核动态分配(如MSI-X中断)。
Perf关联:PMU溢出中断通常占用一个高向量号(如236+),通过perf_event_interrupt入口处理。向量号可在/proc/interrupts中查看。
中断向量表(IDT)结构 异常 & NMI 向量 0–31 #DE, #PF, #BP, #MC 固定分配 硬件 IRQ 向量 32–47 IRQ0 定时器 IRQ1 键盘 IRQ14 IDE 动态 / 软件 向量 48–255 0x80 系统调用 MSI-X 设备 PMU 溢出 IPI 函数调用

2.2 中断描述符与irq_desc

内核使用struct irq_desc管理每个中断源的状态、处理函数链、统计信息和亲和性。每个IRQ号对应一个irq_desc实例,存储在全局数组irq_desc[]中。

// include/linux/irqdesc.h (简化结构)
struct irq_desc {
    struct irq_common_data  irq_common_data;
    struct irq_data         irq_data;
    unsigned int __percpu   *kstat_irqs;    // 每个CPU的中断计数
    irq_flow_handler_t      handle_irq;     // 高层流控处理
    struct irqaction        *action;        // 处理函数链表
    unsigned int            status_use_accessors;
    struct irq_chip         *chip;          // 硬件控制器操作
    struct irq_domain       *irq_data.domain;
    ...
};
字段 作用 延迟分析关注点
handle_irq 高层中断流控(如handle_edge_irq) 流控函数执行时间
action 设备驱动注册的处理函数链表 上半部执行时间
kstat_irqs 每个CPU的中断计数 中断频率与分布
chip 硬件操作(mask/unmask/ack) 硬件访问延迟
性能陷阱:多个设备共享同一IRQ号时,action链表会串行遍历所有处理函数。共享中断的累积执行时间直接影响调度延迟。
irq_desc 结构关系 irq_desc[irq] handle_irq 流控 chip 硬件操作 action 处理函数链 kstat_irqs 统计

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

Linux将中断处理拆分为两个阶段,以平衡实时性与吞吐量:

  • 上半部(Top Half):在中断上下文中立即执行,关中断或屏蔽当前IRQ线。完成硬件ACK、数据拷贝、清除中断标志等紧急操作。必须短小精悍。
  • 下半部(Bottom Half):推迟到开中断后执行,处理耗时逻辑。内核提供三种机制:软中断(SoftIRQ)、tasklet、工作队列(workqueue)。
延迟分析核心:上半部执行期间本地CPU关中断,直接阻塞调度器时钟中断和所有其他中断。上半部执行时间 = 不可抢占的延迟增量。下半部虽然开中断,但软中断/ tasklet仍运行在中断上下文中,优先级高于普通进程。

2.3.1 完整处理流程

  1. CPU收到中断信号,硬件自动压栈SS/RSP/RFLAGS/CS/RIP,跳转到IDT中对应的入口。
  2. 入口代码保存寄存器,调用do_IRQ()或common_interrupt()。
  3. 根据IRQ号找到irq_desc,调用handle_irq流控函数。
  4. 流控函数调用action链表中的handler(上半部)。
  5. 上半部返回后,检查是否有待处理的软中断,执行do_softirq()。
  6. 软中断处理完后,恢复寄存器,执行iretq返回被中断的上下文。
中断处理流程:上半部与下半部 硬件中断到达 入口保存 / do_IRQ 上半部 (Top Half) 关中断 / ACK / 拷贝 检查软中断 (do_softirq) 下半部 (SoftIRQ/Tasklet) iretq 返回 延迟关键点 • 上半部关中断时长 • 软中断优先级 > 进程 • 下半部可被新中断抢占

2.3.2 下半部机制对比

机制 执行上下文 可睡眠 并发模型 典型延迟
SoftIRQ 中断上下文 否 同类型可并行(多CPU) 低(~μs级)
Tasklet 中断上下文 否 串行(同类型单CPU) 中(~10μs级)
Workqueue 进程上下文 是 由kworker线程调度 高(~ms级)
Perf观测点:使用perf sched可以观测workqueue延迟;使用perf stat -e irq_vectors:*可以统计各向量中断频率;使用perf probe可以动态测量上半部执行时间。

2.4 中断上下文与调度延迟的关系

中断上下文与普通进程上下文的关键区别决定了延迟分析的切入点:

  • 不可抢占窗口:上半部执行期间,当前CPU无法响应调度器时钟中断,无法触发进程切换。这个窗口直接累加到调度延迟中。
  • 软中断优先级:软中断在do_softirq()中执行,优先级高于任何用户态进程。大量网络软中断(NET_RX_SOFTIRQ)会显著延迟SCHED_OTHER进程的调度。
  • 中断嵌套:高优先级中断可以抢占低优先级中断的上半部,但所有上半部都关本地中断,因此上半部之间不会嵌套。
典型问题:网卡中断上半部执行时间过长(如大量拷贝),导致音频中断(通常高优先级)被延迟,造成音频卡顿。这是中断上下文驱动延迟的经典案例。