ucos互斥信号量解决优先级反转问题

在可剥夺性的内核中,当任务以独占方式使用共享资源的时候,会出现低优先级任务高于高优先级任务运行的情况,这种情况叫做优先级反转,对于实时操作系统而言,这是一场灾难,下面我们来说说优先级反转的典型环境.

我们假设有三个任务a,b,c,a优先级高于b,b优先级高于c,a和c都需要访问一个共享资源s,保护该资源的信号量为互斥信号量,

假设当前任务c申请了信号量访问s,还没有释放,此时任务a开始运行,那么a就会剥夺c的运行而运行a,当a去访问资源s的时候,因为得不到信号量,所以必须释放以等待信号量,任务c得以重新运行,到这里流程都是正常的,信号量的设计也是为了满足这个功能,但是,当任务c在运行并准备释放信号量的时候,任务b开始运行,那么任务b就要剥夺任务c的运行,这个时候系统就只有b在运行,而a能打断b的运行但是需要信号量,可是c优先级比较低得不到运行,这样,a就只能等到b运行完主动释放使用权才能得到运行了.

到这里问题就发生了,优先级比较高的a在优先级比较低的b运行的时候无法抢断,可剥夺性内核却剥夺不了,系统故障,在这种故障极大地降低了系统的实时性

以上说的情况就是操作系统的优先级反转

而ucos为了解决这种问题,在互斥信号量中引入了优先级提升的方法,他的基本思想是:让当前获得互斥信号量的任务的优先级短暂提升到系统可以接受的最大优先级,尽量让该任务快速的完成并释放信号量,释放之后在恢复为任务原来的优先级别.

原理说完了,接下来我们来看看代码,之前已经说过互斥信号量的部分实现,那部分不再赘述,集中看优先级提升的部分,优先级的提升我们可以猜测应该在一个任务在获取了信号量之后完成的,那也就是说,应该在ospendxxx函数里面

查看OSMutexPend函数,发现其中果然有玄机,如下

pip = (INT8U)(pevent->OSEventCnt >> 8u);

if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) {

pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;

pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;

pevent->OSEventPtr  = (void *)OSTCBCur;

if (OSTCBCur->OSTCBPrio <= pip) {

OS_EXIT_CRITICAL();

*perr = OS_ERR_PIP_LOWER;

} else {

OS_EXIT_CRITICAL();

*perr = OS_ERR_NONE;

}

Pip变量是保存在OSEventCnt中的,当我们创建信号量的时候,就会给定这个值,这个值也就是系统能够将等待该互斥信号量的任务提升的最高优先级

当一个任务请求信号量的时候,如果有信号量空余,将当前请求信号量的任务的优先级放到OSEventCnt的低八位中,

if (OSTCBCur->OSTCBPrio <= pip)

如果当前请求信号量的任务的优先级高于最高提升优先级(数值上低于),那直接运行,没必要提升优先级,否则的话,就要进行下面的操作

mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);

ptcb  = (OS_TCB *)(pevent->OSEventPtr);

if (ptcb->OSTCBPrio > pip) {

if (mprio > OSTCBCur->OSTCBPrio) {

y = ptcb->OSTCBY;

if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != 0u) {

OSRdyTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX;

if (OSRdyTbl[y] == 0u) {

OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;

}

rdy = OS_TRUE;

这一段的意思就是当优先级低于最高可提升优先级的时候,将系统就绪表中的原来的ready标志清除掉,接下来

ptcb->OSTCBPrio = pip;

ptcb->OSTCBY    = (INT8U)( ptcb->OSTCBPrio >> 3u);

ptcb->OSTCBX    = (INT8U)( ptcb->OSTCBPrio & 0x07u);

ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY);

ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX);

if (rdy == OS_TRUE) {

OSRdyGrp               |= ptcb->OSTCBBitY;

OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;

} else {

pevent2 = ptcb->OSTCBEventPtr;

if (pevent2 != (OS_EVENT *)0) {

pevent2->OSEventGrp               |= ptcb->OSTCBBitY;

pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;

}

}

OSTCBPrioTbl[pip] = ptcb;

将当前任务的优先级切换成提升优先级,并把快速访问就绪表的元素的数据改变,同时修改系统就绪表,将提升优先级的任务的新优先级在任务就绪表中设置成就绪,最后,在tcb表中对应pip的位置,设置为提升了优先级的任务的tcb.

这样,任务的优先级就被提升了,系统下一次被调用的时候,就会按照被提升了优先级的任务的新优先级来进行调度.

既然优先级能被提升,那么也应该要能被降下来,而这个降下来应该需要依靠ospostxxx在释放信号量的时候执行,我们查看OSMutexPost代码,可以看到

if (OSTCBCur->OSTCBPrio == pip) {                    OSMutex_RdyAtPrio(OSTCBCur, prio);

}

也就是说,当释放信号量的任务的优先级为等于互斥信号量最高可提升优先级的时候,需要通过OSMutex_RdyAtPrio函数来将任务的优先级恢复,prio的来源是从事件中获取的优先级,大家还记得记得提升之前的优先级保存到了哪里?就是保存在这里, OSEventCnt的低八位

pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;

恢复优先级的函数为OSMutex_RdyAtPrio,核心代码为

y  =  ptcb->OSTCBY;

OSRdyTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX;

if (OSRdyTbl[y] == 0u) {

OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;

}

ptcb->OSTCBPrio  = prio;

OSPrioCur   = prio;

#if OS_LOWEST_PRIO <= 63u

ptcb->OSTCBY   = (INT8U)((INT8U)(prio >> 3u) & 0x07u);

ptcb->OSTCBX   = (INT8U)(prio & 0x07u);

#else

ptcb->OSTCBY    = (INT8U)((INT8U)(prio >> 4u) & 0x0Fu);

ptcb->OSTCBX    = (INT8U) (prio & 0x0Fu);

#endif

ptcb->OSTCBBitY  = (OS_PRIO)(1uL << ptcb->OSTCBY);

ptcb->OSTCBBitX  = (OS_PRIO)(1uL << ptcb->OSTCBX);

OSRdyGrp       |= ptcb->OSTCBBitY;

OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;

OSTCBPrioTbl[prio]      = ptcb;

流程与之前提权其实是类似的,只是一个换成高优先级一个换成低优先级

先将高优先级的任务的任务就绪表中对应的位清除,然后重新设置任务的原始优先级以及当前任务优先级(释放信号量和申请信号量的是同一个任务),然后设置快速访问就绪任务表的数据元素,重新设置任务就绪表,最后将tcb数组中对应原始优先级的数据指针设置到指向任务tcb,这样就实现了任务的恢复.

到这里,就说明白了互斥信号量的优先级提升流程,要注意一点,如果使用互斥信号量的优先级提升,那么那个可提升最高优先级必须不能对应有相应的用户任务,因为ucos不允许两个任务有相同的优先级!切记切记

时间: 2024-08-02 11:01:46

ucos互斥信号量解决优先级反转问题的相关文章

iOS并发编程笔记,包含GCD,Operation Queues,Run Loops,如何在后台绘制UI,后台I/O处理,最佳安全实践避免互斥锁死锁优先级反转等,以及如何使用GCD监视进程文件文件夹,并发测试的方案等

iOS并发编程笔记,包含GCD,Operation Queues,Run Loops,如何在后台绘制UI,后台I/O处理,最佳安全实践避免互斥锁死锁优先级反转等,以及如何使用GCD监视进程文件文件夹,并发测试的方案等 线程 使用Instruments的CPU strategy view查看代码如何在多核CPU中执行.创建线程可以使用POSIX 线程API,或者NSThread(封装POSIX 线程API).下面是并发4个线程在一百万个数字中找最小值和最大值的pthread例子: #import

多进程多线程优先级理解--优先级反转【转】

本文转载自:http://blog.csdn.net/yusiguyuan/article/details/14161061 1. 优先级反转(Priority Inversion)    由于多进程共享资源,具有最高优先权的进程被低优先级进程阻塞,反而使具有中优先级的进程先于高优先级的进程执行,导致系统的崩溃.这就是所谓的优先级反转(Priority Inversion). 2. 产生原因      其实,优先级反转是在高优级(假设为A)的任务要访问一个被低优先级任务(假设为C)占有的资源时,

用信号量解决进程的同步与互斥探讨【持续更新】

现代操作系统采用多道程序设计机制,多个进程可以并发执行,CPU在进程之间来回切换,共享某些资源,提高了资源的利用率,但这也使得处理并发执行的多个进程之间的冲突和相互制约关系成为了一道难题.如果对并发进程的调度不当,则可能会出现运行结果与切换时间有关的情况,令结果不可再现,影响系统的效率和正确性,严重时还会使系统直接崩溃.就比如你只有一台打印机,有两个进程都需要打印文件,如果直接让他们简单地并发访问打印机,那么你很可能什么都打印不出来或者打印的文件是...anyway,我们需要增加一些机制来控制并

优先级反转实验,使用信号量实现【RT-Thread学习笔记 5】

RTOS中很经典的问题.就是在使用共享资源的时候,优先级低的进程在优先级高的进程之前执行的问题.这里模拟这种情况. 下面的实验模拟了优先级反转的情况: 先定义三个线程: //优先级反转实验 rt_sem_t sem; rt_uint32_t t1_count = 0,t2_count = 0,worker_count = 0; rt_thread_t t1,t2,worker ; void pri1_entry(void *parameter) { rt_err_t result; while(

Freertos-事件标志组,消息队列,信号量,二值信号量,互斥信号量

任务间的通信和同步机制  在裸机编程时,使用全局变量的确比较方便,但是在加上 RTOS 后就是另一种情况了. 使用全局变量相比事件标志组主要有如下三个问题: 1.使用事件标志组可以让 RTOS 内核有效地管理任务,而全局变量是无法做到的,任务的超时等机制需要用户自己去实现.2.使用了全局变量就要防止多任务的访问冲突,而使用事件标志组则处理好了这个问题,用户无需担心.3.使用事件标志组可以有效地解决中断服务程序和任务之间的同步问题. 事件标志组:事件标志组是实现多任务同步的有效机制之一. 每创建一

操作系统进程调度,优先级反转,调度策略

转载请注明:http://blog.csdn.net/guo8113/article/details/39645041 在多进程.多线程并发的环境里,从概念上看,有多个进程或者多个线程在同时执行,具体到单个CPU级别,实际上任何时刻只能有一个进程或者线程处于执行状态:因此OS需要决定哪个进程执行,哪些进程等待,也就是进程的调度. 一.调度的目标 1.首先要区分程序使用CPU的三种模式:IO密集型.计算密集型和平衡型.对于IO密集型程序来说,响应时间非常重要:对于CPU密集型来说,CPU的周转时间

FreeRTOS 二值信号量,互斥信号量

本章节讲解 FreeRTOS 任务间的同步和资源共享机制,二值信号量. 二值信号量是计数信号量的一种特殊形式,即共享资源为 1 的情况. FreeRTOS 分别提供了二值信号量和计数信号量,其中二值信号量可以理解成计数信号量的一种特殊形式,即初始化为仅有一个资源可以使用,只不过 FreeRTOS 对这两种都提供了 API函数,而像 RTX,uCOS-II 和 III 是仅提供了一个信号量功能,设置不同的初始值就可以分别实现二值信号量和计数信号量. 当然,FreeRTOS 使用计数信号量也能够实现

用信号量解决生产者消费者问题

用信号量解决生产者消费者问题: ipc.h #ifndef _IPC_H_ #define _IPC_H_ #include <sys/types.h> #include <unistd.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/shm.h> #include <errno.h> #include <stdio.h> #include <st

优先级反转

优先级翻转发生的条件:    1.首先发生翻转需要有三个任务,它们的优先级分别是高.中.低.    2.低优先级和高优先级都需要take同一个信号量. 优先级翻转的过程:    1.低优先级先take到信号量.    2.低优先级被中优先级任务抢占.(然后低优先级任务就只能等中优先级任务执行完,再继续执行之后,才能释放信号量了)    3.高优先级需要take该信号量了,但是由于中优先级任务运行时间长,低优先级任务又霸占住信号量不放,高优先级无奈被阻塞了.(一般高优先级任务都是要快速完成一些需要