1997 年,火星探路者号(Mars Pathfinder)反复重启——原因是优先级反转。一个低优先级任务持有锁,阻塞了高优先级任务,而中优先级任务抢占了低优先级任务 → 死局。
一、先看图
sequenceDiagram
participant H as 高优先级<br/>气象任务
participant M as 中优先级<br/>通信任务
participant L as 低优先级<br/>数据收集
L->>L: 获取 mutex
H->>H: 需要 mutex → 阻塞
M->>M: 抢占 L(优先级更高)
M->>M: 长时间运行
Note over H: H 被 M 间接阻塞<br/>(优先级反转)
Note over L: L 无法运行<br/>无法释放锁
二、优先级反转
2.1 定义
高优先级任务被低优先级任务间接阻塞,因为中优先级任务抢占了持锁的低优先级任务。
2.2 Mars Pathfinder
- 高:气象总线任务(bus scheduler)
- 中:通信任务
- 低:气象数据收集
低持锁 → 高等锁 → 中抢占低 → 高超时 → watchdog 重启。
修复:VxWorks 开启优先级继承。
三、PIP(Priority Inheritance Protocol)
低优先级持锁 → 高优先级等待锁
→ 低优先级临时提升到高优先级
→ 低不会被中抢占
→ 低释放锁 → 恢复原优先级
3.1 嵌套继承
A 持锁 X → B(高)等 X → A 提升。 A 又持锁 Y → C(更高)等 Y → A 再次提升。
优先级传递链(priority chain)。
3.2 限制
- 不防止死锁
- 链长度可能很长 → 分析困难
四、PCP(Priority Ceiling Protocol)
每个锁有一个”天花板优先级”= 所有可能持有者中的最高优先级。
任务获取锁时 → 立即提升到天花板优先级 → 保证不会被任何可能竞争者抢占。
优势:防止死锁(如果天花板设置正确)。 劣势:需要静态分析确定天花板。
五、Linux rt_mutex
// kernel/locking/rtmutex.c
struct rt_mutex {
raw_spinlock_t wait_lock;
struct rb_root_cached waiters; // 按优先级排序
struct task_struct *owner;
};rt_mutex 实现 优先级继承:
- waiter 加入 → 检查 owner 优先级 → 必要时提升
- 支持嵌套继承(priority chain walking)
- 深度限制防止无限递归
六、PREEMPT_RT 的转换
PREEMPT_RT 把 spinlock_t →
rt_mutex_t:
// 普通内核
spin_lock(&lock); // 禁止抢占
// PREEMPT_RT
spin_lock(&lock); // 实际是 rt_mutex_lock → 可睡眠 + 优先级继承只有 raw_spinlock_t 保持真正自旋。
好处:所有内核锁路径都有优先级继承 → 系统级防止优先级反转。
七、glibc PI mutex
pthread_mutexattr_t attr;
pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
pthread_mutex_init(&mutex, &attr);底层用 PI futex → 内核 rt_mutex → 优先级继承。
八、DEADLINE 任务的反转
SCHED_DEADLINE 任务也可能遇到优先级反转:
- DEADLINE 任务等锁 → 持锁的 SCHED_NORMAL 任务被抢占
- rt_mutex 可以提升 SCHED_NORMAL → 但 DEADLINE 带宽保证更复杂
当前内核对 DEADLINE + PI 的支持仍在演进。
九、案例
9.1 音频
JACK audio server 使用 RT 线程 → 锁竞争 → 优先级反转 → 音频卡顿(xrun)。
解决:lock-free ring buffer 或 PI mutex。
9.2 数据库
实时数据库(如 VoltDB)在 RT 调度下需要考虑锁的优先级反转。
十、小结
- 优先级反转 = 高优先级被低优先级间接阻塞
- 优先级继承(PIP):临时提升持锁者优先级
- 优先级天花板(PCP):获锁即提升到最高
- Linux rt_mutex 实现 PIP + 嵌套继承
- PREEMPT_RT 把所有 spinlock 变成 rt_mutex
参考文献
kernel/locking/rtmutex.c- Mike Jones, “What Really Happened on Mars.” 1997
Documentation/locking/rt-mutex-design.rst- Sha et al., “Priority Inheritance Protocols: An Approach to Real-Time Synchronization.” IEEE TC 1990
工具
chrt -f <priority> <command>(设置 RT 优先级)trace-cmd record -e sched_pi_setprio- cyclictest(RT 延迟测试)
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】SCHED_FIFO/RR 与 PREEMPT_RT
Linux 里的实时:SCHED_FIFO/RR 提供优先级调度,但原版内核仍有不可抢占点。PREEMPT_RT 补丁集 20 年后(6.12)合入主线,把几乎所有 spinlock 变 rt_mutex、IRQ 变线程。本文讲 RT 调度语义、RT-throttling、cyclictest 基线、优先级继承、以及 RT 部署的陷阱。
【操作系统百科】实时 OS 巡礼
VxWorks/QNX/Zephyr 与 Linux PREEMPT_RT 的工程取舍——硬实时 vs 软实时、静态调度、ARINC-653 分区、安全认证(DO-178C)、最小内存占用。
【操作系统百科】线程化中断
把 IRQ handler 线程化——PREEMPT_RT 的核心改造之一。本文讲 request_threaded_irq、handler/thread_fn 分工、IRQF_ONESHOT、全线程化的延迟与吞吐代价、softirq 线程化趋势。
【操作系统百科】futex
glibc pthread_mutex 背后是 futex——用户态快路径无 syscall,竞争时才进内核。本文讲 FUTEX_WAIT/WAKE、PI futex、robust futex、futex2、requeue、安全补丁与工程陷阱。