1. 中断基础概念:中断定义、中断向量表、中断控制器

中断是硬件与软件交互的核心机制,允许外设或CPU内部事件异步通知处理器,从而避免轮询带来的CPU浪费。理解中断的定义、向量表结构以及中断控制器(APIC/GIC)的作用,是掌握内核驱动中断处理的基石。

1.1 中断定义与分类

中断(Interrupt)是一种硬件或软件触发的信号,使CPU暂停当前执行流,保存上下文,跳转到预定义的中断服务例程(ISR)执行,完成后恢复原执行流。中断分为以下几类:

分类 触发源 典型示例 特点
外部中断 外设硬件 网卡、磁盘、键盘 异步,由中断控制器路由
内部中断 CPU内部异常 除零、缺页、非法指令 同步,与指令执行直接相关
软中断 软件指令 int 0x80、系统调用 同步,用于陷入内核
IPI(核间中断) 其他CPU核 TLB刷新、任务调度 多核通信专用
关键区分:中断是异步的,异常是同步的。驱动开发中主要处理外部中断,但必须理解异常处理机制,因为中断处理不当可能触发异常(如栈溢出)。

1.2 中断向量表(IVT/IDT)

中断向量表是CPU用于查找中断处理程序入口地址的数据结构。x86架构使用中断描述符表(IDT),ARM架构使用异常向量表(Vector Table)。

IDT结构(x86): 每个中断号对应一个8字节的描述符,包含段选择子、偏移量、类型属性等。CPU收到中断后,根据中断号从IDT中取出描述符,跳转到对应的ISR。

IDT条目格式(64位模式):
struct idt_entry {
    uint16_t offset_low;    // 偏移量低16位
    uint16_t selector;      // 代码段选择子
    uint8_t  ist;           // 中断栈表索引
    uint8_t  type_attr;     // 类型与属性(DPL、Present等)
    uint16_t offset_mid;    // 偏移量中16位
    uint32_t offset_high;   // 偏移量高32位
    uint32_t reserved;      // 保留
} __attribute__((packed));

ARM异常向量表: ARMv8-A架构的异常向量表分为4个表(EL0/EL1/EL2/EL3),每个表包含16个条目,每个条目对应一种异常类型(同步、IRQ、FIQ、SError)。

注意: 中断向量表必须在系统初始化阶段由内核设置,且必须位于内存的特定对齐地址(x86为8字节对齐,ARM为2KB对齐)。驱动开发者通常不直接操作向量表,但需要理解中断号与向量表的映射关系。

1.3 中断控制器的作用

中断控制器是连接外设与CPU的硬件单元,负责中断的接收、优先级仲裁、路由和屏蔽。不同架构使用不同的中断控制器:x86使用APIC(高级可编程中断控制器),ARM使用GIC(通用中断控制器)。

1.3.1 x86 APIC架构

现代x86系统使用两级APIC架构:I/O APIC(负责收集外设中断)和Local APIC(集成在CPU核内,负责向核提交中断)。

组件 位置 功能
I/O APIC 芯片组/主板 接收外设中断线,根据重定向表将中断路由到指定CPU的Local APIC
Local APIC CPU核内 接收I/O APIC或IPI,管理本地中断优先级,向CPU核提交中断
MSI/MSI-X PCIe设备 允许设备直接向Local APIC写入中断消息,绕过I/O APIC

1.3.2 ARM GIC架构

ARM GIC(Generic Interrupt Controller)是ARM体系的标准中断控制器,支持SPI(共享外设中断)、PPI(私有外设中断)和SGI(软件生成中断)。

中断类型 ID范围 目标 说明
SGI 0–15 指定CPU核 用于核间通信(IPI)
PPI 16–31 单个CPU核 每个核私有的外设中断(如本地定时器)
SPI 32–1019 任意CPU核 共享外设中断,可路由到多个核
驱动开发要点: 在设备树(Device Tree)中,中断控制器通过 interrupt-controller 属性标识,外设通过 interrupts 属性指定中断号、触发类型(边沿/电平)和优先级。内核使用 irq_domain 将硬件中断号映射到Linux IRQ号。

1.4 中断处理流程总览

下图展示了从外设触发中断到CPU执行ISR的完整流程,涵盖中断控制器路由和向量表查找两个关键阶段。

中断处理流程总览 外设触发中断 (网卡/磁盘/键盘) 中断控制器 (APIC / GIC) CPU核 (接收中断信号) 中断请求线 路由/仲裁 中断向量表 (IDT / Vector Table) 查找中断号 中断服务例程 (ISR / handler) 跳转执行 恢复执行流 (iret / eret) 返回 说明: 中断控制器负责优先级仲裁和路由,CPU根据中断号从向量表获取ISR入口,执行完成后通过专用指令恢复现场。

1.5 中断优先级与嵌套

中断控制器支持优先级管理,高优先级中断可以抢占低优先级中断的处理。驱动开发者需要关注以下几点:

  • 优先级分组: APIC使用TPR(任务优先级寄存器)和PPR(处理器优先级寄存器)控制;GIC使用二进制点(Binary Point)寄存器配置抢占级别。
  • 中断屏蔽: 在ISR中,可以通过本地中断控制器屏蔽同源或低优先级中断,防止递归或嵌套过深。
  • 嵌套限制: Linux内核默认在ISR中关闭本地中断(local_irq_disable),避免嵌套复杂性;但实时内核(RT-Preempt)允许中断线程化后开启中断。
优先级配置示例(GICv3):
// 设置SPI中断优先级为0x80(范围0x00-0xFF,数值越小优先级越高)
// 在设备树中配置:
interrupts = <0 42 4>;  // 类型(0=SPI) 中断号 触发类型(4=高电平)
// 或在驱动中动态配置:
irq_set_priority(irq_num, 0x80);

1.6 中断上下文与限制

中断处理程序运行在中断上下文中,与进程上下文有显著区别:

特性 中断上下文 进程上下文
可睡眠 否(不能调用 schedule()) 是
可被抢占 否(除非开启内核抢占) 是
持有锁 只能使用自旋锁 可以使用互斥锁
访问用户空间 不允许 允许
执行时间 必须极短(微秒级) 可较长
常见错误: 在中断上下文中调用 kmalloc(GFP_KERNEL) 可能导致睡眠,应使用 GFP_ATOMIC。同样,mutex_lock() 在中断上下文中会导致死锁,必须使用 spin_lock()。

1.7 本章小结