0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

揭秘!基于RT-Thread探究“优先级反转”下的任务调度究竟是什么样的?| 技术集结

RT-Thread官方账号 ? 2025-08-17 10:07 ? 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

本文将基于RT-Thread,结合 RT-Trace 调试器细化到实际任务调度的粒度,来调试并逐步讲解“优先级反转”的调度和运行逻辑。如果对 RT-Trace 感兴趣的可以看这篇文章:

国产嵌入式调试器之光? RT-Trace 初体验!

废话不多说,我们直接开始。本文基于 RT-Thread 来编写测试代码。在此之前我们先捋一下代码流程:

优先级反转问题的本质是高优先级任务因等待低优先级任务释放资源而被阻塞,同时又有其他中优先级任务抢占了 CPU,使得低优先级任务得不到执行机会,进而导致高优先级任务长时间无法继续运行。

因此我们需要建立三个不同优先级的任务,然后需要有一个资源锁来模拟实际场景下对共享资源的获取与释放。一般在 RTOS 中我们会使用互斥锁来对资源进行上锁,因此绝大多数的 RTOS 已经为互斥锁这个组件加入了优先级继承等应对优先级反转问题的功能,包括 RT-Thread(RTT 的互斥锁还具备优先级天花板功能),所以如果我们就是想观察优先级反转的现象,直接使用互斥锁是不行的。

幸运的是,在 RT-Thread 中,还有一个线程间同步组件,其是不带优先级继承等功能的,并且在我们目前的场景中也可以作为资源锁来使用,这就是二值信号量。(特别注意,这边使用二值信号量作为资源锁只是用于观察优先级反转这种异常现象,并且也正是因为其存在这个问题,实际项目中并不推荐这么使用!)

综上,我们的测试代码主体就是三个不同优先级的任务,加上一个资源锁。且我们要测试三种情况,分别是用信号量实现优先级反转,用互斥锁实现优先级继承,在互斥锁的基础上通过设置天花板优先级来实现优先级天花板机制,整体测试代码如下:

#include"rtdef.h"#include"rttypes.h"#include#include#include#include#ifndefRT_USING_NANO#include#endif/* RT_USING_NANO *//* defined the LED0 pin: PB1 */#defineTHREAD_PRIORITY 10#defineUSE_MUTEX 0#defineUSE_PRIORITY_CEILING 0#ifUSE_MUTEXrt_mutex_tmutex;#elsert_sem_tsem;#endifstaticvoidworking(uint32_tnms){ for(volatileuint32_ti =0; i < nms; i++) {? ? ? ? for?(volatileuint32_t?j =?0; j

以上代码动态创建了三个任务:th、tm和tl,分别对应高优先级(9),中优先级(10)和低优先级(11),在 RT-Thread 中优先级数字越小越高。同时还基于条件编译定义了一个资源锁,当USE_MUTEX为0时使用信号量作为锁,用于制造优先级反转,为1且USE_PRIORITY_CEILING为0时使用互斥锁实现优先级继承,USE_MUTEX以及USE_PRIORITY_CEILING都为1时实现优先级天花板策略。

working 函数本质上是制造一段时间的延迟用于模拟任务的工作流程,此处没有使用 rt_thread_mdelay 延时的原因是 rt_thread_mdelay 会造成任务本身主动让出,从而无法实现真实情况下高优先级在低优先级任务运行中的抢占过程。

th 任务首先使用 rt_thread_mdelay 延迟主动让出使得低优先级任务能够先运行从而获取到资源,紧接着尝试获取资源,获取到后使用 working 函数模拟对资源进行的操作,最后释放资源。

tm 任务首先使用 rt_thread_mdelay 延迟主动让出使得高优先级任务能够有时间运行到尝试获取资源并阻塞,从而能够成功制造出中优先级对低优先级任务的抢占。紧接着使用working函数模拟其运行。注意,该任务中没有任何对资源的操作,也不会获取与释放锁。

tl 任务开始运行后立马尝试获取资源,进而使用working函数模拟对资源进行的操作,接着释放资源,最后再次使用working函数模拟该任务的其他操作。

在开启了优先级天花板机制的情况下,互斥锁创建后会使用 rt_mutex_setprioceiling 为其设置一个天花板优先级,在这里也就是 THREAD_PRIORITY - 2,虽然我们所有任务的最高优先级为 THREAD_PRIORITY - 1,由于 RT-Thread 存在时间片轮转调度功能,同优先级任务在时间片用完之后也能互相抢占。此处也可以设置一个比较大的时间片来临时防止抢占,我这里就直接将该优先级设置为更高一级,从根本上避免抢占的发生。

OK,所有功能点都讲解完成,接下来我们就借助 RT-Trace 的运行跟踪功能,进入 CPU 视角,来看看底层的真实运行情况。

首先是使用信号量实现的优先级反转场景,我们重点关注三个任务之间的运行,屏蔽掉中断,空闲线程等其他信息:

e70a91ce-7b0e-11f0-9080-92fbcf53809c.png

可以看到 tl 在开始运行了一段时间后 th 发生了抢占并尝试获取资源,但由于当前资源被 tl 持有,于是 th 立马阻塞让出了 CPU 等待 tl 释放资源:

e71b7a70-7b0e-11f0-9080-92fbcf53809c.png

然而还没等 tl 释放资源,中等优先级的 tm 就抢占了 tl 的运行,此时 th 虽有着最高优先级,但也只能眼睁睁等着 tm 执行完,因为要等 tl 释放资源它才能运行,但 tl 优先级没有 tm 高,当前情况下 tm 不运行完, tl 根本不会运行:

e724c404-7b0e-11f0-9080-92fbcf53809c.png

等 tm 终于运行完了,把 CPU 给到 tl,并且 tl 释放了资源后,本应是最高优先级的 th 才得以运行:

e7330e88-7b0e-11f0-9080-92fbcf53809c.png

由于低优先级任务持有了高优先级任务运行所需的共享资源,从而导致高优先级任务不可避免地进入等待,而当系统比较复杂,任务较多时,低优先级任务又特别容易被其他中优先级任务抢占,最终导致高优先级任务被无限延后,直至所有抢占的任务运行完并且低优先级任务释放资源才得以运行,而这个过程需要等待多久是未知且不可控的。在很多工业、医疗、军事领域中,一个任务之所以要定义为高优先级,就是需要其尽快执行,并且运行周期严格可控,否则很有可能造成不可挽回的后果,如果这个过程中遇到优先级反转,那系统异常甚至崩溃几乎是必然的!

现在我们将 USE_MUTEX 置 1 来使用带有优先级继承功能的互斥锁,此时的任务调度过程如下:

e73ddad4-7b0e-11f0-9080-92fbcf53809c.png

很明显,此时中优先级任务没有能抢占低优先级任务在共享资源处理阶段的运行,虽然高优先级任务第一次尝试获取资源会失败,但这个过程也临时抬升了tl的优先级:

e74aeff8-7b0e-11f0-9080-92fbcf53809c.png

而后 tl 得以不被中优先级的 tm 打断,一路运行到释放资源,紧接着 th 获取资源执行,而 tm 自然是等到 th 运行完后才能运行。很明显,优先级反转带来的影响在这种机制下得到大幅缓解,但这里仍然有一个小瑕疵:

e75360c0-7b0e-11f0-9080-92fbcf53809c.png

在 tl 对共享资源进行处理的过程中,仍然发生了一次抢占,这期间还会动态修改任务的优先级,稍许拉长了tl 对资源的处理时间。如果频繁操作资源,那么这对系统调度来说也是消耗比较大的。如果想要保证高优先级任务能够尽快执行,那么这一段操作一定是要去除的。如何去除?这就引出了下面这个机制。

我们再将 USE_PRIORITY_CEILING 置 1 打开优先级天花板,注意优先级天花板的值是预设且静态的,所以我们在使用这个特性的时候需要提前设置好值:

rt_mutex_setprioceiling(mutex, THREAD_PRIORITY -2);

这个值一般会设置成使用资源中所有任务的最高优先级,当然文章开头也讲过由于 RT-Thread 相同优先级可以通过轮转调度机制相互抢占,我这边为了避免这种情况,所以设置为最高任务优先级的更高一级。

我们来看下加入了优先级天花板后任务的调度情况:

e75f0e34-7b0e-11f0-9080-92fbcf53809c.png

乍一看好像与优先级继承的调度情况没有区别,接下来我将 tl 操作共享资源的时间轴放大:

e7683716-7b0e-11f0-9080-92fbcf53809c.png

对比优先级继承机制下的这部分:

e7758268-7b0e-11f0-9080-92fbcf53809c.png

可以看到中间 th 对 tl 的抢占过程消失了!

e77eafa0-7b0e-11f0-9080-92fbcf53809c.png

正是由于我们设置了天花板优先级,tl 在获取资源的那一刻就将其优先级提升到了天花板,因此在这过程中即使是高优先级的 th 也无法对其进行抢占,真正做到了最快的资源处理速度,自然 th 在这种情况下也得到了最快的响应运行速度。

通过上述内容,相信大家已经对优先级反转,优先级继承以及优先级天花板有了非常直观的理解,在实际的项目中也能够按照实际需求去选择合适的机制。

最后提一点,以上两种方式都是缓解优先级反转带来的影响,并不能解决,因为低优先级任务获取到资源后高优先级任务不可避免地要等待,否则就会造成更为致命的数据同步问题,上述解决方案都只是尽可能让低优先级任务更快速地处理完释放资源从而让高优先级任务及时获取。如果你的系统就不能接受这种情况,那么或许你要从程序设计角度,根本上去避免低优先级任务与高优先级任务共享资源,或是通过精密的设计规避高优先级任务想要获取资源时低优先级任务正在处理资源的情况。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    5161

    文章

    19786

    浏览量

    319722
  • 调试器
    +关注

    关注

    1

    文章

    314

    浏览量

    24428
  • RT-Thread
    +关注

    关注

    32

    文章

    1437

    浏览量

    42326
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    RTOS应用中的优先级反转问题

    在嵌入式系统中,如果使用基于优先级调度算法的RTOS,系统中可能发生优先级反转现象。优先级反转
    发表于 12-14 11:00 ?1638次阅读

    深度剖析 RT-Thread 线程调度流程

    rt_system_scheduler_start:调度系统第一个线程rt_hw_context_switch_to:初始化上下文切换环境,触发PendSV异常first_thread
    的头像 发表于 06-25 18:24 ?836次阅读
    深度剖析 <b class='flag-5'>RT-Thread</b> 线程<b class='flag-5'>调度</b>流程

    请问UCOS如果把一个优先级为5的任务删除后,还能创建另一个优先级为5的任务吗?

    1,ucos 中不允许有相同优先级任务,如果把一个优先级为5的任务删除后,我还可以创建另一个优先级为5的
    发表于 05-12 23:05

    干货 | RTOS应用中的优先级反转问题

    在嵌入式系统中,如果使用基于优先级调度算法的RTOS,系统中可能发生优先级反转现象。优先级反转
    发表于 03-09 15:00

    RT-Thread与UCOS的简单比较

    一、RT-Thread与UCOS的简单比较任务或线程调度RT-Thread可选优先级抢占式调度
    发表于 08-20 06:00

    RT-Thread的内核调度算法实现

    rt-thread调度算法为基于优先级调度和基于时间片轮转调度共存的策略。rt-thread
    发表于 04-20 11:54

    RT-Thread基于优先级的全抢占式调度算法的实现

    一、原理概述RT-Thread 是一款嵌入式实时操作系统(RTOS),同时也是一款优秀的物联网操作系统,相对于裸机的轮询调度算法,它使用的线程(任务调度算法是基于
    发表于 04-20 14:17

    探讨一RT-Thread任务调度的启动顺序

    ;amp;(thread->tlist));(f)调用rt_schedule进行调度。首先需要做的就是查找当前最高的优先级RT-Thread
    发表于 05-09 14:13

    rt-thread优先级的线程可以调度执行吗?

    请教下,在rt-thread中,如果低优先级的线程中用while(1){}直接死循环,是不是高优先级的线程也无法调度执行了?如果高优先级的线
    发表于 05-13 10:51

    RT-Thread线程优先级链表与位图算法的介绍

    1 线程优先级链表每个线程控制块都带有一个链表成员,根据优先级thread->slist插入对相应优先级链表中,对于相同优先级采取时间片轮
    发表于 05-13 15:38

    RT-Thread系统线程调度器的设计实现

    RT-Thread内核中也允许创建相同优先级的线程。相同优先级的线程采用时间片轮转方式进行调度(也就是通常说的分时调度器),时间片轮转
    发表于 08-23 15:24

    如何去处理RT-Thread线程优先级的问题呢

    RT-Thread优先级问题,官方文档互斥量一节,线程2的优先级比线程1高,但在线程2running的时候还是会被线程1抢占,达不到官方文档的仿真运行结果。下图是我的仿真运行结果,输出打印
    发表于 12-05 11:51

    RT-Thread的互斥量优先级问题求解

    优先级最大值25,线程优先级设置为21一25都可恢复正常功能,即高优先级先运行。源代码如下:/*Copyright (c) 2006-2018, RT-Thread Developme
    发表于 12-09 15:43

    任务优先级分配该遵循什么样的原则?

    大家好!一直在做基于RT-Thread的多任务程序开发,但是对于多任务优先级该如何分配,并没有太多的经验。所以想问一大家,在进行多
    发表于 02-10 14:14

    什么是优先级反转

    假设现在有三个任务TaskA(优先级高)、TaskB(优先级中)、TaskC(优先级低),一个信号量(Semaphore),此信号量用于任务
    的头像 发表于 04-24 13:01 ?2886次阅读
    什么是<b class='flag-5'>优先级</b><b class='flag-5'>反转</b>