优先级反转(Priority Inversion)

优先级反转 是 实时操作系统(RTOS) 和 多任务并发 里经常遇到的经典问题。 

有一个实际案例是 NASA 的 火星探路者号(Mars Pathfinder, 1997年) 就因为 优先级反转的bug 导致系统多次重启,最后NASA工程师远程上传了“优先级继承协议”补丁, 才解决问题。

一. 什么是优先级反转

现象是:

当一个  低优先级任务  正在执行, 这时 一个高优先级任务 需要马上运行。但是高优先级的任务因为必须等待 低优先级的任务释放某种资源(比如共享数据、设备等),而 低优先级任务又被一个中优先级的任务抢占,从而导致高优先级任务实际上以低优先级的速度运行,整个系统的优先级调度秩序被“反转”了的异常情况。

二. 具体例子

假设系统里有三个任务:

  • 高优先级任务 H:实时性要求高(比如传感器采样)。

  • 中优先级任务 M:普通任务(比如日志处理)。

  • 低优先级任务 L:后台任务(比如数据打包)。

场景:

  1. 任务 L 占用了一个共享资源(比如串口、互斥锁)。

  2. 任务 H 运行时也需要这个资源,于是 被阻塞,等待 L 释放

  3. 这时任务 M 就绪,它优先级比 L 高 → CPU 把时间片给 M。

  4. 结果:L 没机会释放资源 → H 只能干等 → 系统错过实时期限。

这就是 优先级反转

三. 解决办法

  • 优先级继承 (Priority Inheritance Protocol, PIP)    ✅ 常用

    • 当低优先级任务 L 占用资源,而高优先级任务 H 在等待时 → 临时把 L 的优先级提升到 H 的级别,直到 L 释放资源。

    • 这样 M 就不会插队。

  • 优先级上限 (Priority Ceiling Protocol, PCP)

    • 给每个资源分配一个优先级上限。

    • 占用资源的任务会自动提升到该上限,避免被中间优先级任务打断。

  • 避免共享资源 / 锁粒度太大

    • 尽量减少临界区。

    • 使用无锁算法(lock-free)、消息队列代替互斥锁。

下面是优先级继承的一个实现的示例:

在 RTOS 里,互斥锁 (mutex) 是支持优先级继承的核心。
当一个任务尝试获取 mutex 时:

  1. 如果 mutex 空闲 → 直接获取。

  2. 如果 mutex 已被低优先级任务持有 → 高优先级任务会阻塞。

  3. RTOS 检测到 高优先级任务在等待 → 临时提升持有者的优先级

数据结构示例:

typedef struct Task {
    int base_priority;     // 任务的原始优先级
    int current_priority;  // 任务的当前优先级(可能被提升)
    bool waiting;          // 是否在等待资源
    // ... 其他任务控制块信息 (TCB)
} Task;

typedef struct Mutex {
    Task* owner;           // 当前持有该锁的任务
    Queue waiting_list;    // 等待该锁的任务队列
} Mutex;

带优先级继承的mutex加锁 和 解锁:

void mutex_lock(Mutex* m, Task* t) {
    if (m->owner == NULL) {
        // 锁空闲,直接获取
        m->owner = t;
    } else {
        // 锁被占用
        enqueue(&m->waiting_list, t);
        t->waiting = true;

        // 优先级继承逻辑
        if (t->current_priority > m->owner->current_priority) {
            m->owner->current_priority = t->current_priority;
            // 可能需要触发调度器重排任务
            scheduler_reschedule();
        }
    }
}

void mutex_unlock(Mutex* m, Task* t) {
    if (m->owner != t) {
        // 非法解锁
        return;
    }

    if (!is_empty(&m->waiting_list)) {
        // 有任务在等待
        Task* next = dequeue(&m->waiting_list);
        m->owner = next;
        next->waiting = false;
    } else {
        // 没有等待者,锁空闲
        m->owner = NULL;
    }

    // 恢复优先级
    t->current_priority = t->base_priority;
    scheduler_reschedule();
}

调度器如何配合:
调度器需要根据current_priority选任务,而不是 base_priority

Task* scheduler_pick_next() {
    // 在就绪队列中找 current_priority 最高的任务
    Task* best = find_highest_priority_task(ready_queue);
    return best;
}

执行过程示例:

  1. 低优先级 L 获取了 mutex。

    owner = L,L 的 current_priority = 1
  2. 高优先级 H 试图获取 mutex,被阻塞。

    • H 进入 waiting_list。

    • 检查:H.priority (3) > L.priority (1) → 提升 L.current_priority = 3

  3. 调度器运行 → L(临时优先级=3)继续运行 → 完成任务并释放 mutex。

  4. 解锁时 → H 被唤醒,L 恢复 base_priority=1

四. 优先级继承总结

  • 互斥锁记录拥有者

  • 当高优先级任务等待时,提升持有者优先级

  • 调度器依据 current_priority 运行任务

  • 释放锁后恢复优先级

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值