1. 概念

    一个高优先级线程通过信号量机制访问共享资源时,该信号量已被一个低优先级线程占有,而这个低优先级线程在访问共享资源时被其他的一些中等优先级线程抢占,因此造成高优先级线程被许多具有较低优先级的线程阻塞,称此现象为优先级反转。
    优先级反转会导致低优先级任务先于高优先级任务运行,在实时系统中会导致不可控的现象发生,因此,优先级反转在实时系统中是不可接受的。

2. 解决优先级反转

    高优先级任务不能执行的原因是因为低优先级占用了资源,而低优先级不能获得CPU,无法释放资源,所以解决优先级反转的原则是让低优先级任务尽快执行,释放资源。
SylixOS下解决优先级反转有优先级天花板策略(priority ceiling)和优先级继承策略(priority inheritance),且SylixOS下互斥信号量同时支持这两种策略。

2.1 优先级天花板

    优先级天花板策略是指将占有某资源的任务的优先级提高,提升到可能访问该资源的所有任务中最高优先级任务的优先级(这个优先级称为该资源的优先级天花板)。

2.2 优先级继承

    当发现高优先级的任务因为低优先级任务占用资源而阻塞时,就将低优先级任务的优先级提升到等待它所占有的资源的最高优先级任务的优先级。

3. 互斥量解决优级反转

3.1 互斥量属性块

    互斥量在创建时,需先创建互斥量属性块,SylixOS下互斥量属性块结构如程序清单 3.1所示。

程序清单 3.1 互斥量属性块结构

typedef struct {
    int          	PMUTEXATTR_iIsEnable;     	/*  此属性块是否有效           */
    int             	PMUTEXATTR_iType;       	/*  互斥量类型                 */
    int             	PMUTEXATTR_iPrioceiling;	/*  天花板优先级               */
    unsigned long	PMUTEXATTR_ulOption;       	/*  算法类型                   */
} pthread_mutexattr_t;

    初始化互斥量时,如果不为互斥量属性块进行初始化,则使用默认属性,SylixOS互斥量默认属性块如程序清单 3.2所示。

程序清单 3.2 默认互斥量属性块

static const pthread_mutexattr_t  _G_pmutexattrDefault = {
    1,
    PTHREAD_MUTEX_DEFAULT,                       	/*  允许递归调用               */
    PTHREAD_MUTEX_CEILING,
    (LW_OPTION_INHERIT_PRIORITY | 
    LW_OPTION_WAIT_PRIORITY) 				/*PTHREAD_PRIO_NONE	       */
};

    由默认互斥量默认属性块可知,SylixOS下默认解决优先级反转方案是优先级继承策略。

3.2 提高优先级

    低优先级的任务占有资源,且高任务的优先级请求资源时,会尝试提高低优先级任务的优先级,在申请资源时调用_EventPrioTryBoost函数尝试提高任务优先级。

#include <SylixOS.h>
VOID  _EventPrioTryBoost (PLW_CLASS_EVENT  pevent, PLW_CLASS_TCB  ptcbCur)

    函数_EventPrioTryBoost原型分析:

  • 参数pevent为资源所属的事件控制块;

  • 参数ptcbCur为当前任务控制块。

    该函数根据互斥量属性块的属性设置,确定选用优先级继承策略或者天花板策略(SylixOS下默认是使用优先级继承策略),调用_SchedSetPrio函数改变任务的优先级。

3.3 恢复优先级

    低优先级任务释放互斥量时,调用EventPrioTryResume尝试恢复任务的优先级。

#include <SylixOS.h>
VOID  _EventPrioTryResume (PLW_CLASS_EVENT  pevent, PLW_CLASS_TCB   ptcbCur)

    函数_EventPrioTryResume原型分析:

  • 参数pevent为资源所属的事件控制块;

  • 参数ptcbCur为当前任务控制块。

    低优先级任务删除互斥量时,会尝试恢复任务优先级,代码片段如程序清单 3.3所示。

程序清单 3.3 尝试恢复优先级

if (ptcb) {
        ucPriority = (UINT8)pevent->EVENT_ulMaxCounter;	       /*  获得原线程优先级           */
	/*
	 *  拥有者优先级发生了变化,还原优先级
	 */
        if (!LW_PRIO_IS_EQU(ucPriority, ptcb->TCB_ucPriority)) {
        _SchedSetPrio(ptcb, ucPriority);
    }
}
4. 优先级设置函数

    在使用互斥量时,尝试提高或恢复任务优先级,调用SchedSetPrio函数进行优先级设置。

#include <SylixOS.h>
VOID  _SchedSetPrio (PLW_CLASS_TCB  ptcb, UINT8  ucPriority)

    函数_SchedSetPrio原型分析:

  • 参数ptcb为当前任务控制块;

  • 参数ucPriority为需设置的优先级。

    _SchedSetPrio函数改变任务的优先级,实现流程如图 4.1所示。

 SylixOS下优先级反转与解决方案_SylixOS   优先级反转

图 4.1 优先级设置流程