一. 任务优先级反转的概念
例如:
1. 任务A和任务C都要使用同一共享资源S,现在假设任务A和任务B都在等待与各自任务相关的事件发生而处于等待状态,而任务C正在运行,且在t1时刻取得了信号量的并开始访问共享资源S。
2. 如果在任务C使用共享资源S过程中的t2时刻,任务A等待的事件到来,那么由于任务A的优先级别高于任务C,所以任务A就剥夺C的CPU而进入运行状态,而使任务C中止运行,这样C就失去了释放信号量的机会。
3. 如果任务A在运行中的t3时刻也要访问共享资源S,但是由于任务C还未释放信号量,因此任务A只好等待。
4. 上面三步出没什么问题,但是如果在任务C继续使用共享资源S过程中的t4时刻,任务B所等待的事件也来临了,由于任务B的优先级也高于任务C,任务B当然要剥夺任务C的CPU而进入运行状态,任务C则只好等待。
5. 同样,任务A也只有等待B运行结束,并且任务C释放信号量的t6时刻才能运行。
用代码表示如下:
//优先级 MyTask > YouTask > HerTask
void MyTask(void *pdata)
{
for (;;)
{
OSTimeDlyHMSM(0,0,0,200); /* 等待200ms */
OSSemPend(Fun_Semp, 0, &err); /*request signal */
/* Do something */
OSSemPost(Fun_Semp); /* send signal */
OSTimeDlyHMSM(0,0,0,200); /* 等待200ms */
}
}
void YouTask(void *pdata)
{
for (;;)
{
/* Do something */
OSTimeDlyHMSM(0,0,0,300); /* 等待300ms */
}
}
void HerTask(void *pdata)
{
for (;;)
{
OSSemPend(Fun_Semp, 0, &err); /*request signal */
for(Times; Times<20000000;Times++)
{
/* Do something */
OS_Sched();
}
OSSemPost(Fun_Semp); /* send signal */
OSTimeDlyHMSM(0,0,1,0); /* 等待1s */
}
}
运行结果如下:
二. 互斥信号量
使用信号量的任务是否能够运行, 受到任务的优先级别以及 是否占用信号量两个条件约束, 而信号量的约束高于优先级别的约束。
于是,当出现低优先级别的任务与高优先级别的任务使用同一个信号量,而系统中还存在其他中等优先级别的任务时,如果低优先级别的任务先获得了信号量,就会使高优先级别的任务处于等待状态,而那些不使用该信号的中等待优先级别的任务却可以剥夺低优先级别任务的CPU使用权而先于高优先级别的任务而运行了。
解决问题的办法之一是:
使获得信号任务的优先级别在使用共享资源期间暂时提升到所有任务最高优先级的高一个级别上(最高优先级+1),以使该任务不被其他任务所打断,从而能尽快地使用完共享资源并释放信号量,然后在释放信号量之后,再恢复该任务原来的优先级别。
互斥信号量:
互斥信号量就是按上面的解决办法设计的,在描述互斥信号量的事件控制块中,除成员OSEventType=OS_EVENT_TYPE_MUTEX以表明这是一个互斥信号量,成员OSEventCnt被分成了低8位和高8位两部分;
低8位用来存放信号值(0xff时信号有效,即信号量尚未被任何任务所占用;否则信号为无效);
高8位用来存放为了避免出现优先级反转现象而要提升的优先级别。
相关操作:
//参数prio表明要提升的优先级别
OS_EVENT *OSMutexCreate (INT8U prio, INT8U *err);
2. 请求互斥型信号量
void OSMutexPend (OS_EVENT *pevent, INT16U timeout, INT8U *err);
3. 发送互斥型信号量
INT8U OSMutexPost (OS_EVENT *pevent);
4. 获取互斥型信号量的当前状态
INT8U OSMutexQuery (OS_EVENT *pevent, OS_MUTEX_DATA *pdata);
5. 删除互斥型信号量
OS_EVENT *OSMutexDel (OS_EVENT *pevent, INT8U opt, INT8U *err);