FreeRTOS 之 优先级翻转

支持原创,转载请附上原文链接


0、平台

FreeRTOS源码版本:V9.0.0

1、引言

RTOS历史上,有一个非常有名bug,“What Happened on Mars?”。 这里说的比较详细,参见:https://www.dazhuanlan.com/2020/02/09/5e3f9f811e02a/

开始下面的正文之前,先讲述2个RTOS中常用的概念:互斥量与信号量

1.1、互斥量

互斥量一般用于共享资源加锁,防止多个task同时访问一个共享资源,比如:多个task都会修改的全局变量,可以通过互斥量加锁。互斥量一般具有如下特性:

1、优先级继承

2、互斥量不能在中断中使用

3、互斥量获取和释放需要再同一个task中

1.2、信号量:

信号量分为计数信号量和二值信号量,一般用于同步,应用场景比如一个任务等待信号量,另一个任务或者中断释放一个信号量,信号量相比于互斥量:

1、没有优先级继承

2、可以在中断中使用

此外二值信号量,也可以用于资源锁加锁,其优缺点如下:

优点:

1、可以支持中断中使用

2、可以在其他任务释放

缺点:

1、如果其他task在使用共享资源,此时中断获取锁,会立即返回,且结果失败,需要注意单独处理

2、因为可以被其他task释放,因此使用过程中需要注意

2、什么是优先级翻转以及其危害

RTOS不同于桌面系统,其特点就是高实时性。其中优先级就是用来保障实时性的手段之一。但在某些场景下,高优先级的任务可能会被挂起不能执行,我们来看看下面场景:

有taskA、taskB、taskC 3个任务,优先级为taskA > taskB > taskC ,有一个共享资源shareData(shareData可能是全局内存、I/O等等)。现在看看如下场景:

1、在time1时刻taskC ready请求获取shareData资源;

2、在time2时刻taskA ready且也请求获取shareData资源,由于shareData资源被taskC占用,taskA再次进入阻塞等待状态;

3、在time3时刻taskB ready,开始抢占taskC的执行权限,此时taskC进入阻塞状态,taskB开始执行

上述场景是否看出啥问题?

taskA的任务优先级高于taskB,但是由于taskA等待请求获取 shareData资源,且taskC持有了shareData资源,因此低优先级的taskB执行了,但是高优先级的taskA被挂起了。

再进一步,试想一下,如果taskB不是一个task,而是一系列优先级介于taskA和taskC之间的任务列表呢?

上述这种由于等待被低优先级持有的共享资源而进入非预期的长久阻塞状态的问题,就是优先级翻转。

优先级翻转在RTOS中,可能会产生不可预期的危害,比如:“What Happened on Mars?”

3、FreeRTOS的规避策略

很多RTOS都有解决优先级翻转的策略,而且基本原理类似,这里结合FreeRTOS来讲述下RTOS是如何尽量规避优先级翻转的

3.1、优先级继承

结合上面的栗子来说说优先级继承的概念:

在高优先级的taskA获取资源锁时,将taskC的优先级临时提高为taskA的优先级,那么上述案例中,taskB就无法打断taskC的执行,因此taskC执行完成释放资源锁后,taskA能及时的进入ready状态,并执行

那么下面结合FreeRTOS源码,我们来看看优先级继承是如何实现的。

3.1.1、第一次交锋

taskC获取互斥锁时,执行了如下代码段:

#if ( configUSE_MUTEXES == 1 )

{

    if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )

    {

        /* Record the information required to implement

        priority inheritance should it become necessary. */

        pxQueue->pxMutexHolder = ( int8_t * ) pvTaskIncrementMutexHeldCount();

    }

    else

    {

        mtCOVERAGE_TEST_MARKER();

    }

}

上面代码大致意思是,如果是互斥锁,调用函数pvTaskIncrementMutexHeldCount()设置互斥锁持有者,函数pvTaskIncrementMutexHeldCount()代码如下:

void *pvTaskIncrementMutexHeldCount( void )

{

    /* If xSemaphoreCreateMutex() is called before any tasks have been created

    then pxCurrentTCB will be NULL. */

    if( pxCurrentTCB != NULL )

    {

        ( pxCurrentTCB->uxMutexesHeld )++;

    }

    return pxCurrentTCB;

}

结合上面两段代码,第一次持有互斥锁时,执行了如下操作:

1、将当前任务快的uxMutexesHeld自加加,表示已经有人持有了互斥锁

2、将当前任务的任务控制块返回赋值给MutexHolder

3.1.2、换将再战

当taskA获取互斥锁时,由于已经被人持有,所以,进入阻塞等待状态,此时执行了如下代码:

if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX )

{

    taskENTER_CRITICAL();

    {

        vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder );

    }

    taskEXIT_CRITICAL();

}

其中函数vTaskPriorityInherit() 部分代码如下:

void vTaskPriorityInherit( TaskHandle_t const pxMutexHolder )

{

TCB_t * const pxTCB = ( TCB_t * ) pxMutexHolder;

        /* 如果Mutex holder的优先级低于当前等待任务优先级,则进行优先级继承 */

        if( pxTCB->uxPriority < pxCurrentTCB->uxPriority )

        {

            if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )

            {

                listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority );

            }

            else

            {

                mtCOVERAGE_TEST_MARKER();

            }

            if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )

            {

                if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )

                {

                    taskRESET_READY_PRIORITY( pxTCB->uxPriority );

                }

                else

                {

                    mtCOVERAGE_TEST_MARKER();

                }

                /* Inherit the priority before being moved into the new list. */

                pxTCB->uxPriority = pxCurrentTCB->uxPriority;

                prvAddTaskToReadyList( pxTCB );

            }

            else

            {

                /* Just inherit the priority. */

                pxTCB->uxPriority = pxCurrentTCB->uxPriority;

            }

            traceTASK_PRIORITY_INHERIT( pxTCB, pxCurrentTCB->uxPriority );

        }

}

结合上面的分析,大致梳理清楚了优先级继承的实现流程。但是战役还未结束:

taskC的优先级何时恢复呢?

3.2、优先级恢复

优先级恢复流程相对比较简单,在taskC使用完,调用释放接口的时候,会执行优先级恢复,此时taskC继续恢复其低优先级。流程如下:

xSemaphoreGive() -->xQueueGenericSend() -->prvCopyDataToQueue() -->xTaskPriorityDisinherit()

代码也比较简单,这里就不贴出来分析

4、引申互动

4.1、为什么Mutex 互斥量不能中断中调用?

我的理解大概如下几点:

1、中断不能因为等待阻塞的互斥量而进入阻塞状态

2、中断中调用互斥量,会打破优先级继承的里面

4.2、为什么Semaphore 信号量不支持优先级继承?

我同样用栗子来解释这个问题:

有A、B、C三个任务,优先级A > B > C

现在time1时刻C任务开始等待信号量sem_kk, time2时刻A任务也开始等待信号量 sem_kk,time3时刻任务B释放了一个信号量sem_kk。(time3 > time2 > time1)

那问现在来看看如下问题:

此时RTOS中是希望A还是C先执行呢?

答案很明显,是希望A先执行,因为A的任务优先级更高。

但是如果信号量实现了优先级继承,结果会如何呢?

C在等待信号量sem_kk的过程中优先级临时继承了A的优先级,此时A、C优先级相同,无法确保A一定是优先于C执行的了

因此

信号量一般是用于同步的,同步的场景上,需要保证优先级高的任务优先执行,做到真正的实时性。 优先级继承会打破这个需求

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,776评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,527评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,361评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,430评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,511评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,544评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,561评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,315评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,763评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,070评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,235评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,911评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,554评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,173评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,424评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,106评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,103评论 2 352