3. 中断优先级定义:硬件优先级与软件优先级的区别、优先级数值与抢占行为

中断优先级是嵌入式系统响应实时事件的核心机制。在 Linux 内核与嵌入式驱动开发中,优先级并非单一概念,而是由硬件中断控制器与内核软件逻辑共同构成的二层体系。理解这两者的区别、数值映射关系以及抢占行为,是配置中断响应延迟与嵌套策略的基础。

核心要点:硬件优先级决定中断信号能否被 CPU 识别并挂起;软件优先级决定中断处理函数(ISR)在 CPU 上执行的调度顺序。两者共同决定一个中断请求的最终响应行为。

3.1 硬件优先级与软件优先级的区别

对比维度 硬件优先级 软件优先级
定义层级 中断控制器(GIC、NVIC、APIC)内部逻辑 Linux 内核 IRQ 子系统(struct irq_desc)
决定因素 中断号、硬件连线、寄存器配置 irq_set_priority()、线程优先级、亲和性
作用时机 中断信号到达 CPU 前,决定是否被屏蔽或抢占 中断信号已送达 CPU,决定 ISR 执行顺序与嵌套
抢占能力 高优先级硬件中断可抢占低优先级硬件中断 高软件优先级 ISR 可抢占低软件优先级 ISR(需内核配置)
典型实现 ARM GIC 的 Priority Register、x86 APIC 的 TPR Linux tasklet、workqueue、线程化 IRQ 优先级
驱动开发者控制 通过设备树或平台数据设置中断触发类型与优先级 通过 request_threaded_irq()、sched_setscheduler() 调整
实战提示:在 ARM Cortex-A 系列 + GIC-400 平台上,硬件优先级数值越小优先级越高(0 为最高)。Linux 内核默认将软件优先级映射为 0-255 范围,但驱动开发者通常只使用 0-15 或 0-31 的细分区间。

3.2 优先级数值与抢占行为

优先级数值是硬件与软件之间沟通的“数字语言”。数值的编码方式、比较规则直接决定了中断能否抢占当前正在执行的中断处理程序。

3.2.1 硬件优先级数值模型

以 ARM GICv2 为例,硬件优先级寄存器为 8 位宽,但实际有效位由 Binary Point 寄存器控制。常见配置下,优先级数值分为抢占优先级与子优先级两部分。

Binary Point 设置 抢占优先级位数 子优先级位数 抢占组数 典型应用
BP=0 7 位 1 位 128 强实时系统,精细抢占
BP=3 4 位 4 位 16 通用嵌入式 Linux
BP=7 0 位 8 位 1 无抢占,轮询模式

抢占规则:只有当新中断的抢占优先级数值小于当前正在服务的中断的抢占优先级数值时,硬件才会触发抢占。子优先级仅在相同抢占优先级的中断同时到达时用于裁决服务顺序。

常见误区:许多开发者误以为“数值越大优先级越高”。在 ARM GIC 和 x86 APIC 中,数值越小优先级越高。而在一些老式 MCU(如 8051)或 DSP 中,数值越大优先级越高。移植驱动时务必确认中断控制器的数值编码方向。

3.2.2 软件优先级与线程化 IRQ 抢占

Linux 内核将硬件中断处理分为上半部(hardirq)和下半部(softirq / tasklet / threaded IRQ)。软件优先级主要体现在线程化 IRQ 的调度优先级上。

/* 示例:为线程化 IRQ 设置实时优先级 */
struct sched_param param = { .sched_priority = 50 };
struct irq_thread_data *data;

/* 在 probe 函数中注册线程化中断 */
ret = request_threaded_irq(irq_num, hardirq_handler,
                           thread_fn, IRQF_TRIGGER_RISING,
                           "my_device", dev);

/* 获取线程化 IRQ 的内核线程 PID */
data = irq_get_thread_data(irq_num);

/* 设置为 SCHED_FIFO 实时调度策略,优先级 50 */
sched_setscheduler(data->task, SCHED_FIFO, &param);

软件优先级抢占行为遵循 Linux 内核调度规则:

  • SCHED_FIFO:高优先级线程会立即抢占低优先级线程,直到高优先级线程主动休眠或被更高优先级中断唤醒。
  • SCHED_RR:同优先级线程按时间片轮转,高优先级仍可抢占。
  • SCHED_OTHER:普通分时调度,不保证实时抢占。

3.3 硬件与软件优先级协同工作流程

下图展示了从外设中断信号触发到 ISR 在 CPU 上执行的完整优先级决策路径。

硬件与软件优先级协同工作流程 硬件与软件优先级协同工作流程 外设中断信号 中断控制器(GIC/NVIC)硬件优先级仲裁 CPU 响应中断,进入 IRQ 模式 Linux 内核 IRQ 子系统(软件优先级) ISR / 线程化 IRQ 执行 说明: 硬件优先级决定中断能否被 CPU 识别(节点 2);软件优先级决定 ISR 在 CPU 上的调度顺序(节点 4)。

3.4 优先级配置实战要点

配置清单:在驱动中配置中断优先级时,请按以下步骤操作:
  1. 查阅硬件手册:确认中断控制器的优先级数值方向(0 最高还是 255 最高)。
  2. 设置 Binary Point:根据系统实时需求,配置抢占优先级与子优先级的位数分配。
  3. 分配硬件优先级:在设备树或平台数据中为每个中断分配唯一的抢占优先级组。
  4. 注册线程化 IRQ:使用 request_threaded_irq() 将耗时处理下沉到内核线程。
  5. 调整线程优先级:通过 sched_setscheduler() 设置 SCHED_FIFO 及合适的优先级数值。

3.4.1 设备树中的硬件优先级配置示例

/* 设备树片段:为 SPI 控制器中断设置硬件优先级 */
spi@ff110000 {
    compatible = "vendor,spi-controller";
    reg = <0xff110000 0x1000>;
    interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
    /* 在 GIC 驱动中,通过 interrupt-affinity 或
     * 自定义属性设置抢占优先级组 */
    arm,priority = <0x80>;  /* 抢占优先级 = 0x80 (128),子优先级 = 0 */
    interrupt-parent = <&gic>;
};

3.4.2 优先级配置不当的典型问题

问题现象 根本原因 解决方案
高优先级中断响应延迟抖动 硬件优先级数值设置过大,被低优先级中断屏蔽 将高优先级中断的硬件数值设为 0-31 区间
线程化 IRQ 被普通进程抢占 未设置 SCHED_FIFO 或优先级过低 使用 sched_setscheduler() 提升至实时优先级
中断嵌套导致栈溢出 硬件优先级允许过多嵌套层级 调整 Binary Point 减少抢占组数,或使用 IRQF_DISABLED
相同优先级中断互相阻塞 子优先级未配置,所有中断处于同一抢占组 设置 Binary Point 分配子优先级位,或使用软件优先级区分

3.5 优先级数值映射关系图

下图展示了从硬件寄存器数值到软件调度优先级的典型映射关系,帮助开发者直观理解数值转换过程。

优先级数值映射关系 优先级数值映射关系(ARM GICv2 → Linux 内核) 硬件优先级寄存器(8 位) 抢占优先级 [7:4] — 4 位,16 级 子优先级 [3:0] — 4 位,16 级 Linux 内核 IRQ 优先级映射 IRQ 描述符优先级(0-15) 线程化 IRQ 调度优先级(0-99) 映射规则: 硬件抢占优先级 0 → 内核 IRQ 优先级 0(最高);硬件抢占优先级 15 → 内核 IRQ 优先级 15(最低)。 线程化 IRQ 的调度优先级需独立设置,不自动继承硬件优先级数值。

3.6 本章小结

  • 硬件优先级由中断控制器管理,决定中断信号能否被 CPU 识别并挂起,数值越小优先级越高(ARM GIC 体系)。
  • 软件优先级由 Linux 内核 IRQ 子系统管理,决定 ISR 在 CPU 上的执行顺序,可通过 sched_setscheduler() 调整。
  • 抢占行为由硬件优先级数值比较结果触发,软件优先级仅在线程化 IRQ 中生效。
  • 数值映射需要驱动开发者手动建立硬件抢占优先级与内核调度优先级之间的对应关系,避免配置冲突。