优先级反转

本文深入探讨了RTOS中优先级反转现象的发生原因及解决方案,详细解释了优先级继承和优先级极限两种常见解决方法。

优先级翻转发生的条件:
    1、首先发生翻转需要有三个任务,它们的优先级分别是高、中、低。
    2、低优先级和高优先级都需要take同一个信号量。

 
    优先级翻转的过程:
    1、低优先级先take到信号量。
    2、低优先级被中优先级任务抢占。(然后低优先级任务就只能等中优先级任务执行完,再继续执行之后,才能释放信号量了)
    3、高优先级需要take该信号量了,但是由于中优先级任务运行时间长,低优先级任务又霸占住信号量不放,高优先级无奈被阻塞了。(一般高优先级任务都是 要快速完成一些需要及时处理的事情,被阻塞可是大事)
    这样的现象就叫优先级翻转,因为高优先级它无法抢占低的,不“高”了,当然就是翻转了。
 
    避免优先级翻转的办法:
    使用互斥信号量,并且设置防优先级翻转参数(SEM_INVERSION_SAFE )。
 
    防止优先级翻转的原理:
    改变在于上面的过程中的第1步,低优先级take到该信号量的时候,系统就搜索该信号量还可能被哪些任务使用,然后就把该低优先级任务的优先级调整到这些 任务中最高的一个那么高。
    这样,起码低优先级运行到释放信号量,高优先级也就立即能运行了,就不会出现中优先级任务长时间运行使得高优先级任务无休止等待的情况了。
   
    注意两个前提条件:
    1、信号量持有时间尽可能短。(这是写代码过程中就要注意的,做到这一点,就可以保证高优先级不用等低优先级任务太久)
    2、高优先级任务执行非常快。(这也是任务设计的要求,高优先级应该只处理那些紧急而且快速的事情。)
 
    所以不会出现中优先级抢占,但无法抢占而带来问题。因为上述两点决定了它们耽误的时间非常短,不会对中优先级造成影响。



1 问题的提出

  目前,市场上占有率比较高的商业RTOS有VxWorks/PSOS、QNX、 LynxOS、VRTX,、Windows CE等。这些为数众多的RTOS绝大多数都是多任务实时微内核的结构,采用的是基于优先级的可抢占式调度策略。系统为每一个任务分配一个优先权,调度程序 保证当前运行的进程是优先权最高的进程。但是,有时候会出现一种比较奇怪的现象:由于多进程共享资源,具有最高优先权的进程被低优先级进程阻塞,反而使具 有中优先级的进程先于高优先级的进程执行,导致系统的崩溃。这就是所谓的优先级反转(Priority Inversion)。

2 优先级反转

  RTOS普遍具有2个特点:实时性和多任务。实时是指系统的响应时间必须在规定的时间内,超出这个时间限制将会使系 统出现致命的错误;同时,实时性还要求对时间要求非常急迫的任务要先于对时间不是很紧急的任务执行。正是由于这2个原因,RTOS的进程调度普遍采用的是 基于优先级的可抢占式PBP (Priority Based Preemptive)的调度策略。多任务是嵌入式系统的内在要求。如今的嵌入式系统普遍要求具有多任务并发执行的能力,因此RTOS中也必须提供多任务 并发执行的支持。由于多任务并发,必然会导致多个任务共享资源。如有2个任务task1和task2并发执行,都需要向打印机输出结果。由于只有1台打印 机,所以在某个时间段内只能有1个任务,如task1占有打印机并向打印机输出,而这时另一个任务task2处于等待状态。当task1输出完毕后, task2由等待转为就绪,当RTOS再次调度它时,方可占有打印机向打印机输出。试想如果不采取这种方式,不对打印机这种共享资源加以控制,而让 task1和task2同时向打印机输出,这时候打印机打印的结果谁也看不懂,是一堆乱七八糟的东西。因此,大多数的RTOS采用了一种称作信号量 (semaphore)的机制来实现对共享资源的管理。任何一个想使用临界资源(如打印机等共享资源)的进程在进入临界区(如task1或task2中访 问临界资源的代码)之前必须拥有使用临界资源的信号量,否则不可以执行临界区代码。假设系统中有3个任务,分别为task1、task2和task3。 task1的优先权高于task2,而task2的优先权高于task3。恰在此时task1和task2 因某种原因被阻塞,这时候系统调度task3执行。task3执行一段时间后,task1被唤醒。由于采取的是PBP的调度策略,因此task1抢占 task3的CPU, task1执行。task1执行一段时间后要进入临界区,但此时task3占有此临界资源的信号量。因此task1被阻塞,处于等待状态,等待task3 释放此信号量。经过这么一段时间后,task2此时此刻处于就绪状态。因此系统调度task2执行。如果task3在task2的执行期间一直没有能够被 调度执行的话,那task1和task3将一直等到task2执行完后才能执行,task1更要等到task3释放它所把持的信号量才能执行;而这段时间 完全有可能超出task1的Deadline,使得task1崩溃。当系统看到有高优先级的任务崩溃时候,系统认为此时有重大事故发生,为了挽救系统,看 门狗电路起作用,系统可能被自动复位。从上面的分析可以看到,导致系统崩溃的原因是由于优先级高的任务task1要获取被低优先级任务task2占有的临 界资源而被task2阻塞,而具有中优先级的任务task2抢占task3的CPU,从而导致task2先于task1执行。这时候系统便出现了优先级反 转的情况,如图1所示。
优先级反转示意图

3 优先级反转的解决方法

  目前解决优先级反转有许多种方法。其中普遍使用的有2种方法:一种被称作优先级继承 (priority inheritance);另一种被称作优先级极限(priority ceilings)。

  在优先级继承方案 中,当高优先级任务在等待低优先级的任务占有的信号量时,让低优先级任务继承高优先级任务的优先级,即把低优先级任务的优先权提高到高优先级任务的优先 级;当低优先级任务释放高优先级任务等待的信号量时,立即把其优先权降低到原来的优先权。采用这种方法可以有效地解决上面所述的优先权反转的问题。当高优 先级任务task1想要进入临界区时,由于低优先级任务task3占有这个临界资源的信号量,导致task1被阻塞。这时候,系统把task3的优先权升 到task1的优先权,此时优先权处于task1和task3之间的任务task2,即使处于就绪状态也不可以被调度执行,因为此时task3的优先权已 经高于task2,所以task3此时被调度执行。当task3释放task1需要的信号量时,系统立即把task3的优先权降到原来的高度,来保证 task1和task2正常有序执行。整个情况如图2所示。目前,有许多RTOS是采用这种方法来防止优先级反转的,如大家比较熟悉的业界有名的 WindRiver公司的VXWORKS。

采用优先级继承消除优先级反转

在优先权极 限方案中,系统把每一个临界资源与1个极限优先权相联系。这个极限优先权等于系统此时最高优先权加1。当1个任务进入临界区时,系统便把这个极限优先权传 递给这个任务,使得这个任务的优先权最高;当这个任务退出临界区后,系统立即把它的优先权恢复正常,从而保证系统不会出现优先权反转的情况。如上例中,当 task3进入临界区时,立即把它的优先权升高到极限优先权,保证task3此时能尽快退出临界区,进而释放其占有的信号量。当高优先级任务 task1执行的时候就不会出现其等待低优先级任务task3释放信号量而被阻塞的情况,从而保证不会出现上面所说的优先级反转。采用这种方案的另一个有 利之处,是仅仅通过改变某个临界资源的优先级就可以使多个任务共享这个临界资源,如下所示。

void TaskA(void){

...

SetTaskPriority(RES_X_PRIO);

// 访问共享资源 X.

SetTaskPriority(TASK_A_PRIO);

...

}

   以上就RTOS中优先级反转问题出现的原因以及解决方法进行了详细的说明。21世纪将是嵌入式系统的时代。从事嵌入式系统设计的人员深入了解RTOS的 原理和内部潜在的问题,如优先级反转等,将有助于开发出更加可靠的产品。

### 优先级反转问题的定义与影响 优先级反转(Priority Inversion)是指在实时操作系统(RTOS)中,高优先级任务因等待低优先级任务持有的资源而被阻塞,导致中优先级任务先于高优先级任务执行的现象。这种现象会破坏系统的调度顺序,影响任务的实时性可预测性。 在典型的优先级反转场景中,一个低优先级任务持有一个互斥锁(Mutex),而高优先级任务需要访问该锁。此时,高优先级任务会因无法获取锁而进入等待状态。与此同时,中优先级任务可能会抢占低优先级任务的执行,导致低优先级任务无法及时释放锁,进一步延迟高优先级任务的执行。这种行为会显著降低系统的实时性能,甚至可能引发任务超时或功能异常。 ### 优先级反转的解决方案 为了解决优先级反转问题,实时操作系统通常采用两种主流协议:优先级继承协议(Priority Inheritance Protocol, PIP)优先级天花板协议(Priority Ceiling Protocol, PCP)。 #### 优先级继承协议(PIP) 优先级继承协议通过动态调整任务优先级来缓解优先级反转问题。当一个高优先级任务尝试获取由低优先级任务持有的资源时,低优先级任务优先级会被临时提升至高优先级任务优先级。这种优先级提升可以防止中优先级任务抢占低优先级任务,从而加速资源释放,减少高优先级任务的等待时间。 该协议的特点包括动态调整优先级、局部影响较小的运行开销。然而,它也可能导致任务优先级频繁变化,增加系统复杂性,尤其是在多个高优先级任务同时竞争资源的情况下。 #### 优先级天花板协议(PCP) 优先级天花板协议则通过静态设定资源的优先级上限来预防优先级反转。每个共享资源(如互斥锁)都有一个固定的“天花板优先级”,该优先级通常等于或高于所有可能访问该资源的任务的最高优先级。当一个任务获取该资源时,其优先级会被提升至该资源的天花板优先级,直到释放资源后恢复原优先级。 该协议的优点在于能够有效避免复杂的优先级反转潜在的死锁问题,同时简化调度器的设计。但其缺点是需要预先分析系统中所有可能的任务优先级,配置较为复杂,并且不能灵活适应任务优先级的动态变化。 ### 示例代码:优先级继承协议的应用 以下是一个简单的FreeRTOS代码示例,展示如何使用互斥锁(Mutex)实现优先级继承协议: ```c SemaphoreHandle_t xMutex; void vTaskA(void *pvParameters) { while (1) { // 获取互斥锁 if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) { // 执行临界区代码 // ... // 释放互斥锁 xSemaphoreGive(xMutex); } } } void vTaskB(void *pvParameters) { while (1) { // 获取互斥锁 if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) { // 执行临界区代码 // ... // 释放互斥锁 xSemaphoreGive(xMutex); } } } void main(void) { // 创建互斥锁 xMutex = xSemaphoreCreateMutex(); // 创建任务A(低优先级) xTaskCreate(vTaskA, "TaskA", configMINIMAL_STACK_SIZE, NULL, 1, NULL); // 创建任务B(高优先级) xTaskCreate(vTaskB, "TaskB", configMINIMAL_STACK_SIZE, NULL, 3, NULL); // 启动调度器 vTaskStartScheduler(); } ``` 在该示例中,如果任务B(高优先级)因任务A(低优先级)持有互斥锁而无法执行,FreeRTOS会自动提升任务A的优先级任务B的优先级,以防止中优先级任务干扰资源释放过程。 ### 优先级反转问题的实际影响 在实际应用中,优先级反转可能导致系统响应延迟、任务超时甚至功能失效。例如,在工业控制系统或航空航天设备中,实时性要求极高,优先级反转可能导致关键任务无法按时执行,从而影响整个系统的安全性可靠性。因此,合理设计资源访问机制调度策略对于避免优先级反转至关重要。 ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值