当前位置: 首页 > ops >正文

优先级反转问题

文章目录

    • 优先级反转的发生过程
      • **阶段 1:初始状态 & 低优先级任务获取锁**
      • **阶段 2:高优先级任务就绪并请求锁**
      • **阶段 3:高优先级任务请求同一把锁 - 阻塞!**
      • **阶段 4:调度器切换回低优先级任务 - 但被中优先级任务插队!**
      • **阶段 5:中优先级任务长时间运行**
      • **阶段 6:中优先级任务完成 & 低优先级任务终于运行并释放锁**
      • **核心问题总结:优先级反转的关键时刻在哪里?**
    • 优先级继承如何解决问题
      • 原始问题场景回顾(未启用优先级继承):
      • 启用优先级继承后的解决流程:
        • 阶段 1: Task_L 获取锁(不变)
        • 阶段 2: Task_H 请求锁被阻塞 → **触发优先级继承!**
        • 阶段 3: Task_M 就绪 → **无法再抢占 Task_L!**
        • 阶段 4: Task_L 释放锁 → **优先级恢复 + 唤醒 Task_H**
        • 阶段 5: 正常调度恢复
      • 关键效果对比:
      • 优先级继承的核心逻辑:
      • 为什么能根治优先级反转?

优先级反转的发生过程

假设系统中有三个任务,按优先级从高到低排列:

  1. Task_H:最高优先级任务(例如优先级 10)
  2. Task_M:中优先级任务(例如优先级 20)
  3. Task_L:最低优先级任务(例如优先级 30)

它们需要访问同一个共享资源(比如一段共享内存或一个硬件外设),该资源必须通过互斥锁(Mutex)保护,确保同一时刻只有一个任务能访问。用 Mutex_A 表示这个锁。


阶段 1:初始状态 & 低优先级任务获取锁

  • 时间 t0:
    • Task_L(低优先级)处于就绪状态或正在运行。
    • Task_HTask_M 可能处于挂起(等待事件)或就绪状态,但尚未被调度器选中运行。
    • 共享资源目前未被占用。
  • 事件: Task_L 需要访问共享资源。
  • 系统动作: Task_L 成功调用 OSMutexPend(Mutex_A, timeout, &err)
  • 结果:
    • Task_L 获取Mutex_A
    • Task_L 进入临界区,开始操作共享资源。
    • Mutex_A 的状态标记为Task_L 持有

阶段 2:高优先级任务就绪并请求锁

  • 时间 t1:
    • Task_L 仍在临界区内运行(尚未完成对共享资源的操作,未释放锁)。
    • 事件Task_H(高优先级)变为就绪状态(例如,它等待的事件发生了,如定时器到期或收到消息)。
  • 系统动作(调度器介入):
    • 调度器发现 Task_H(优先级 10)比当前运行的 Task_L(优先级 30)优先级更高
    • 调度器立即抢占 Task_L 的 CPU。
    • Task_L 的状态由运行变为就绪
    • Task_H 的状态由就绪变为运行
  • 结果:
    • Task_H 开始执行。
    • Task_L 暂停在临界区中(它仍然持有 Mutex_A 锁)。

阶段 3:高优先级任务请求同一把锁 - 阻塞!

  • 时间 t2:
    • Task_H 正在运行。
    • Task_H 很快也需要访问同一个共享资源。
  • 事件: Task_H 调用 OSMutexPend(Mutex_A, timeout, &err) 尝试获取锁。
  • 系统动作:
    • 内核检查 Mutex_A 的状态,发现它当前正被 Task_L 持有
    • 因为锁已被占用,Task_H 无法立即获取锁
    • 内核将 Task_H 的状态由运行改为阻塞(或称为“挂起”、“等待”),并将其放入 Mutex_A等待队列中。
    • Task_H 现在在等待 Task_L 释放锁
    • 调度器再次被触发,寻找新的就绪任务执行。
  • 关键点:高优先级任务 Task_H 现在被锁卡住了,它在等待低优先级任务 Task_L 完成工作并释放锁

阶段 4:调度器切换回低优先级任务 - 但被中优先级任务插队!

  • 时间 t3:
    • 调度器发现阻塞的 Task_H 无法运行。
    • 调度器查找最高优先级的就绪任务。此时,之前被抢占的 Task_L(优先级 30)仍然处于就绪状态(它在时间 t1 被 Task_H 抢占)。
    • 事件:就在调度器即将把 CPU 交还给 Task_L,让它可以继续执行、完成临界区工作并最终释放锁(从而唤醒 Task_H)时,意想不到的事情发生Task_M(中优先级,优先级 20)突然变为就绪状态(例如,它等待的事件发生了)。
  • 系统动作(调度器被迫再次决策):
    • 调度器比较当前可运行任务的优先级:
      • Task_L (优先级 30 - 就绪)
      • Task_M (优先级 20 - 新就绪状态)
    • Task_M 的优先级 (20) 比 Task_L 的优先级 (30) 更高
    • 调度器选择运行 Task_M
  • 结果:
    • Task_M 的状态由就绪变为运行
    • Task_L 再次无法执行!它仍然停留在临界区中,仍然持有 Mutex_A 锁。
    • 阻塞的 Task_H 仍然在等待锁。

阶段 5:中优先级任务长时间运行

  • 时间 t4:
    • Task_M 运行中(执行它自己的任务代码,完全与共享资源无关)。
    • Task_L 保持就绪但无法运行(被 Task_M 压着)。
    • Task_H 保持阻塞(等待锁)。
  • 问题爆发:
    • 此时,本该是系统最高优先级任务的 Task_H,其执行完全取决于 Task_M 何时完成!本质上,Task_M(中优先级)变相地阻塞了 Task_H(高优先级)
    • 系统的优先级规则被破坏:优先级关系本该是 H > M > L,但现在实际的执行顺序变成了 M > L,而 H 被迫等待 L(而 L 又被迫等待 M),最终 HM 间接阻塞。

阶段 6:中优先级任务完成 & 低优先级任务终于运行并释放锁

  • 时间 t5:
    • Task_M 完成其工作,进入挂起状态(例如,等待下一个事件)。
  • 系统动作:
    • 调度器再次被触发,寻找最高优先级就绪任务:此时只有 Task_L(优先级 30)是就绪状态。
    • 调度器选择运行 Task_L
  • 结果:
    • Task_L 恢复执行,从它被第二次抢占的地方继续。
  • 时间 t6:
    • Task_L 完成了对共享资源的操作,离开临界区
    • 事件: Task_L 调用 OSMutexPost(Mutex_A) 释放 Mutex_A 锁。
  • 系统动作:
    • 内核发现 Mutex_A 的等待队列中有更高优先级的任务 Task_H
    • 内核唤醒 Task_H(将其状态由阻塞改为就绪)。
    • 调度器被立即触发。
    • 调度器发现 Task_H(优先级 10)是最高优先级就绪任务。
    • 调度器抢占仍在运行的 Task_L(优先级 30)。
    • 调度器将 CPU 交给 Task_H
  • 结果:
    • Task_H 终于获得锁并进入临界区执行。
    • Task_L 再次被抢占(状态变为就绪)。

核心问题总结:优先级反转的关键时刻在哪里?

关键在于 阶段 4(时间 t3)。此时:

  1. Task_H 被锁阻塞,在等待 Task_L 释放锁。
  2. 调度器正准备运行 Task_L(让它可以去释放锁)。
  3. 一个完全不相关、但优先级处于 HL 之间的任务 Task_M 恰好在这个最坏的时机就绪了。

由于调度器的规则是无条件优先执行当前最高优先级的就绪任务,它必须Task_M 运行(因为 20 > 30)。这就导致:

  • 持有锁的 Task_L 无法继续执行
  • 迫切需要锁的 Task_H 被迫等待的时间被极大地延长了——其等待时间不再是 Task_L 剩下的临界区执行时间(本应很短),而是变成了:Task_M 的全部执行时间 + Task_L 剩下的临界区执行时间。

这就是“反转”——原本应该拥有最高执行权力的 Task_H,其命运被一个比自己优先级还低(但比持锁者高)的任务 Task_M 所主宰,导致系统实时性丧失,关键的高优先级任务可能错过其截止时间 (Deadline)。 火星探路者号频繁重启就是这个原因导致的。解决方案(如优先级继承)就是通过在内核层面动态调整持锁任务的优先级,来防止 Task_M 在这种关键时刻插队。

优先级继承如何解决问题

优先级继承(Priority Inheritance)的核心思想是:当高优先级任务因锁被阻塞时,内核临时提升持有该锁的低优先级任务的优先级,使其免受中优先级任务干扰,从而加速锁的释放。

原始问题场景回顾(未启用优先级继承):

  1. Task_L(低)持有锁 → Task_H(高)请求锁被阻塞 → Task_M(中)抢占 Task_L → Task_H 被迫等待 Task_M 执行完成
    结果:Task_H 的等待时间被无限拉长。

启用优先级继承后的解决流程:

阶段 1: Task_L 获取锁(不变)
  • t0: Task_L(优先级 30)获取互斥锁 Mutex_A,进入临界区。
阶段 2: Task_H 请求锁被阻塞 → 触发优先级继承!
  • t1:

    • Task_H(优先级 10)就绪并抢占 Task_L。
    • Task_H 请求 Mutex_A,发现锁被 Task_L 持有。
  • 结果:

    • Task_L 优先级临时提升至 10(与 Task_H 相同)
    • Task_H 进入阻塞态,等待锁释放

关键点:此时 Task_L 的优先级 = 10,不再是原来的低优先级(30)

阶段 3: Task_M 就绪 → 无法再抢占 Task_L!
  • t2:
    • Task_M(优先级 20)变为就绪状态
  • 调度器决策:
    • 当前就绪任务:Task_L(优先级 10),Task_M(优先级 20)
    • 比较规则:数值越小优先级越高
      → 优先级 10 > 优先级 20
      Task_L 继续运行!
  • 结果:
    • Task_M 无法抢占 Task_L(因其优先级低于 Task_L 的临时优先级 10)
    • Task_L 持续占有 CPU 执行临界区代码
阶段 4: Task_L 释放锁 → 优先级恢复 + 唤醒 Task_H
  • t3:
    • Task_L 完成临界区操作,释放锁 OSMutexPost(Mutex_A)
  • 结果:
    1. Task_L 优先级恢复为 30
    2. Task_H(优先级 10)被唤醒,获得锁并开始执行
    3. 调度器立即切换至 Task_H(因它优先级最高)
阶段 5: 正常调度恢复
  • Task_H 执行临界区代码(持有锁)
  • 完成后释放锁 → 调度器按正常优先级调度后续任务

关键效果对比:

场景Task_H 等待时间系统行为
无优先级继承Task_M执行时间 + Task_L剩余时间Task_H 被中优先级阻塞
启用优先级继承仅 Task_L 剩余执行时间Task_M 无法中断锁持有者

📊 性能提升:Task_H 的阻塞时间从 T_taskM + T_taskL_remain 缩短为 T_taskL_remain

优先级继承的核心逻辑:

  1. 触发条件

    • 高优先级任务因请求被持有的互斥锁(Mutex) 而阻塞
  2. 继承动作

    • 内核临时将锁持有者的优先级提升至与该阻塞任务相同
  3. 防干扰效果

    • 阻止任何优先级低于继承值的任务(如 Task_M)抢占锁持有者
  4. 恢复机制

    • 锁释放时自动恢复锁持有者的原始优先级
    • 唤醒等待队列中优先级最高的任务

为什么能根治优先级反转?

通过动态改写优先级规则打破死循环:

当锁被低优先级任务持有时,内核临时赋予它“高优先级身份”,使中优先级任务失去剥夺权,保证锁持有者能无干扰地快速完成工作,从根本上消除了中优先级任务间接阻塞高优先级任务的可能性。

http://www.xdnf.cn/news/17794.html

相关文章:

  • 基于阿里云音频识别模型的网页语音识别系统实现
  • Flink中基于时间的合流--双流联结(join)
  • 【Doris】-工具SQLConverter
  • Stagehand深度解析:从开源自动化工具到企业级RPA平台的演进之路
  • VisualStudio2022调试Unity C#代码步骤
  • 第2篇_Go语言基础语法_变量常量与数据类型
  • Android项目中Ktor的引入与使用实践
  • 在 Linux 服务器搭建Coturn即ICE/TURN/STUN实现P2P(点对点)直连
  • 图论Day3学习心得
  • 无脑整合springboot2.7+nacos2.2.3+dubbo3.2.9实现远程调用及配置中心
  • 计算机网络 THU 考研专栏简介
  • L2 级别自动驾驶 硬件架构设计
  • LeetCode 922.按奇偶排序数组2
  • ElasticSearch不同环境同步索引数据
  • Spring Ai 如何配置以及如何搭建
  • Jmeter自定义脚本
  • 零基础学会制作 基于STM32单片机智能加湿系统/加湿监测/蓝牙系统/监测水量
  • 探索无人机图传技术:创新视野与无限可能
  • 在 macOS 上顺利安装 lapsolver
  • OpenCV Python——VSCode编写第一个OpenCV-Python程序 ,图像读取及翻转cv2.flip(上下、左右、上下左右一起翻转)
  • 死锁总结及解决方案
  • 关于截屏时实现游戏暂停以及本地和上线不同步问题
  • 用GPT解释“GPT-5”是什么,有什么优势
  • python-pycharm切换python各种版本的环境与安装python各种版本的环境(pypi轮子下载)
  • Flink Stream API 源码走读 - map 和 flatMap
  • KNN(k近邻算法)
  • Chrome插件开发实战:从架构到发布全流程
  • 准备用Qt6 重写音视频会议系统服务端
  • 开源 Arkts 鸿蒙应用 开发(十五)自定义绘图控件--仪表盘
  • 开源 Arkts 鸿蒙应用 开发(十六)自定义绘图控件--波形图