2. PREEMPT_RT 补丁架构:内核抢占模型演进与关键特性

PREEMPT_RT 补丁并非凭空创造,而是建立在 Linux 内核抢占模型逐步演进的基础之上。理解从无抢占到完全实时抢占的演进路径,是掌握中断控制器适配的前提。本章将系统梳理四种抢占模型,并深入剖析 PREEMPT_RT 的三大核心机制:中断线程化、自旋锁可抢占与高精度定时器。

2.1 内核抢占模型演进

Linux 内核主线提供了四种可选的抢占配置,从左到右实时性逐级增强。下表从抢占时机、适用场景与延迟特性三个维度进行对比:

抢占模型 抢占时机 适用场景 最大延迟典型值
PREEMPT_NONE 仅在返回用户空间时 服务器、批处理系统 10~100 ms
PREEMPT_VOLUNTARY 内核代码中显式调度点 桌面系统 1~10 ms
PREEMPT 任意内核上下文(除临界区) 嵌入式、多媒体 100 μs~1 ms
PREEMPT_RT 任意上下文(含中断与锁内) 工业控制、航空航天 1~20 μs
演进逻辑: 从 PREEMPT_NONE 到 PREEMPT_RT,本质上是将“不可抢占区域”逐步缩小,直至几乎消除所有非抢占路径。PREEMPT_RT 补丁的核心工作就是通过中断线程化与锁机制改造,将内核变为完全可抢占。

下图展示了四种模型下内核代码执行流的抢占边界差异:

抢占模型演进对比 PREEMPT_NONE 不可抢占(内核态) 可抢占(用户态) PREEMPT_VOLUNTARY 不可抢占(临界区) 可抢占(调度点+用户态) PREEMPT 不可抢占(锁内) 可抢占(内核+用户态) PREEMPT_RT 完全可抢占 (含中断与锁内) PREEMPT_RT 通过中断线程化与可抢占自旋锁,将不可抢占区域压缩到极致 使最高优先级任务的最坏响应时间从毫秒级降至微秒级

2.2 中断线程化(IRQ Threading)

中断线程化是 PREEMPT_RT 最核心的改造之一。传统 Linux 中,硬件中断处理程序(ISR)运行在不可抢占的上下文(硬中断上下文),即使高优先级实时任务也无法抢占它。PREEMPT_RT 将大部分中断处理逻辑移入内核线程,使中断处理可被调度器管理。

2.2.1 中断线程化架构

中断线程化后的处理流程分为两个阶段:

  1. 上半部(硬中断): 仅执行最必要的操作(如清除中断标志、读取硬件状态),然后唤醒对应的中断线程。
  2. 下半部(线程化): 在可抢占的线程上下文中执行实际的数据处理、协议解析等工作,受调度器优先级控制。
中断线程化处理流程 硬件中断触发 上半部(硬中断) 唤醒中断线程 线程化处理 调度器优先级仲裁 中断线程化后,高优先级实时任务可以抢占低优先级中断线程 硬中断上下文仅保留微秒级操作,其余全部移入可抢占线程

2.2.2 中断线程化的配置与验证

在 PREEMPT_RT 内核中,中断线程化通过内核参数 threadirqs 控制,默认开启。可以通过以下方式查看中断线程状态:

# 查看所有中断及其对应的线程
cat /proc/interrupts

# 查看特定中断线程的优先级
ps -eo pid,comm,pri,rtprio | grep irq

# 示例输出:irq/50-eth0 线程运行在实时优先级 50
 1234 irq/50-eth0     50   50
关键注意: 并非所有中断都适合线程化。例如时钟中断(timer)和 IPI(处理器间中断)必须保留在硬中断上下文,否则会导致系统死锁。PREEMPT_RT 通过 irq_settings 标志位区分可线程化与不可线程化的中断。

2.3 自旋锁可抢占(Preemptible Spinlocks)

传统自旋锁在持有期间会禁止内核抢占,导致实时任务无法抢占锁持有者。PREEMPT_RT 将自旋锁改造为可睡眠的互斥锁(rt_mutex),使锁持有者可以被更高优先级的任务抢占。

2.3.1 锁机制对比

锁类型 传统内核 PREEMPT_RT 抢占行为
spin_lock 禁止抢占 + 自旋等待 映射为 rt_mutex 可被高优先级任务抢占
spin_lock_irq 禁止本地中断 + 禁止抢占 映射为 rt_mutex + 关中断 中断关闭期间不可抢占
raw_spin_lock 与 spin_lock 相同 保持原始自旋锁行为 不可抢占(用于底层代码)
mutex 可睡眠互斥锁 保持不变 可抢占
适配要点: 在编写中断控制器驱动时,必须明确区分 spin_lock 与 raw_spin_lock 的使用场景。与硬件寄存器直接交互的临界区必须使用 raw_spin_lock,因为 spin_lock 在 PREEMPT_RT 下可能睡眠,而硬件访问不允许睡眠。

2.3.2 自旋锁可抢占的实现原理

PREEMPT_RT 通过以下机制实现自旋锁的可抢占:

  1. 锁类型重映射: 将 spinlock_t 结构体扩展为包含 rt_mutex 字段的复合结构。
  2. 优先级继承: 当低优先级任务持有锁时,高优先级任务等待该锁,系统会将低优先级任务临时提升到高优先级,防止优先级反转。
  3. 锁内抢占点: 在持有 spin_lock 的代码路径中插入抢占检查点,允许更高优先级的任务在锁内被调度。
自旋锁可抢占机制 传统 spin_lock 禁止抢占 → 自旋等待 高优先级任务被阻塞 PREEMPT_RT spin_lock 映射为 rt_mutex 可抢占 + 优先级继承 实时任务可抢占 最坏延迟 < 20 μs 无优先级反转 代码示例:中断控制器驱动中正确使用锁类型 硬件寄存器访问 → raw_spin_lock_irqsave() 数据队列操作 → spin_lock_irqsave()

2.4 高精度定时器(High-Resolution Timers)

PREEMPT_RT 将内核定时器从基于 jiffies 的粗粒度定时器升级为高精度定时器(hrtimer),提供纳秒级定时精度。这对于中断控制器适配至关重要,因为许多实时应用依赖精确的定时中断来触发采样或控制动作。

2.4.1 hrtimer 与传统定时器对比

特性 传统 timer_list hrtimer
精度 jiffies 粒度(通常 1~10 ms) 纳秒级(受硬件时钟源限制)
时钟源 系统节拍时钟(tick) 硬件高精度定时器(如 TSC、HPET)
回调上下文 软中断(softirq) 可配置:硬中断 / 软中断 / 线程化
中断控制器适配 依赖 tick 中断 可绑定到独立硬件定时器中断线

2.4.2 hrtimer 在中断控制器适配中的应用

在 PREEMPT_RT 系统中,中断控制器驱动可以利用 hrtimer 实现以下功能:

  • 精确延迟测量: 使用 hrtimer 测量中断响应时间,验证实时性能。
  • 周期性中断模拟: 当硬件中断控制器不支持周期性中断时,通过 hrtimer 模拟固定周期的中断触发。
  • 超时管理: 为中断处理设置精确超时,防止硬件挂死导致系统无响应。
// 中断控制器驱动中使用 hrtimer 的典型模式
#include <linux/hrtimer.h>
#include <linux/ktime.h>

struct my_irq_ctrl {
    struct hrtimer timer;
    // ... 其他字段
};

// 定时器回调函数
static enum hrtimer_restart my_timer_callback(struct hrtimer *timer)
{
    struct my_irq_ctrl *ctrl = container_of(timer, struct my_irq_ctrl, timer);
    
    // 执行周期性中断处理
    // ...
    
    // 重新启动定时器(周期模式)
    hrtimer_forward_now(timer, ktime_set(0, 1000000)); // 1ms 周期
    return HRTIMER_RESTART;
}

// 初始化 hrtimer
void my_irq_ctrl_init(struct my_irq_ctrl *ctrl)
{
    hrtimer_init(&ctrl->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    ctrl->timer.function = my_timer_callback;
}
最佳实践: 在 PREEMPT_RT 系统中,hrtimer 的回调默认运行在硬中断上下文。如果回调需要执行较长时间(超过 100 μs),应使用 HRTIMER_MODE_SOFT 标志将其切换到软中断上下文,或配合中断线程化使用。

2.5 三大关键特性的协同关系

中断线程化、自旋锁可抢占与高精度定时器并非独立工作,而是相互协同,共同构建实时内核的基础设施:

PREEMPT_RT 三大关键特性协同 PREEMPT_RT 内核 中断线程化 中断处理可被调度 自旋锁可抢占 锁内可被高优任务抢占 高精度定时器 纳秒级定时精度 三者共同将最坏中断延迟从毫秒级降至微秒级,为中断控制器适配提供实时基础
适配总结: 中断控制器驱动开发者在 PREEMPT_RT 环境下需要关注三个适配点:
1. 中断处理函数必须考虑线程化后的优先级设置;
2. 硬件寄存器访问必须使用 raw_spin_lock 而非 spin_lock;
3. 利用 hrtimer 实现精确的定时中断管理,替代传统的 jiffies 定时器。