在任哲的书本《嵌入式实时操作系统uc/osII原理与应用》第五章所描述的,互斥型信号量将占用共享资源的任务提升到当前最高的优先级,使其能够完整的执行完毕,然后再恢复原先的优先级;这样避免了仅仅使用信号量而出现优先级反转的现象,(当然互斥型信号量就是出于这个目的设计的吧)。
所以在书里的程序里面设计了这样三个任务。优先级为6的任务MyTask、优先级为7的任务YouTask,和优先级为8的HerTask。
HerTask一开始占用了优先级为3的互斥信号量Semp,并且占用时间比较长。
/*****利用互斥信号量消除了优先级反转的现象**************/
#include "includes.h"
#define TASK_STK_SIZE 512 //任务堆栈长度
OS_STK MyTaskStk[TASK_STK_SIZE]; //定义任务堆栈区
OS_STK YouTaskStk[TASK_STK_SIZE]; //定义任务堆栈区
OS_STK HerTaskStk[TASK_STK_SIZE]; //定义任务堆栈区
OS_STK StartTaskStk[TASK_STK_SIZE];//
char *s1="MyTask running";
char *s2="YouTask running";
char *s3="HerTask running";
char *ss="MyTask pend_semp";
INT8U err;
INT8U y=0;
INT32U Times=0;
INT16S key; //用于退出uCOS_II的键
OS_EVENT *Semp; //声明事件控制块
void MyTask(void *pdata); //声明任务
void YouTask(void *pdata); //声明任务
void HerTask(void *pdata); //声明任务
void StartTask(void *pdata);
void QuitKey(void);
/***************主函数*****************/
void main (void)
{
OSInit(); //初始化uCOS_II
PC_DOSSaveReturn( ); //保存Dos环境
PC_VectSet(uCOS, OSCtxSw); //安装uCOS_II中断
Semp=OSMutexCreate(3,&err); //定义信号量
OSTaskCreate(StartTask, //创建任务MyTask
(void*)0, //给任务传递参数
&StartTaskStk[TASK_STK_SIZE - 1], //设置任务堆栈栈顶指针
5); //使任务的优先级别为5
OSStart(); //启动多任务管理
}
/*************任务StartTask***************/
void StartTask (void *pdata)
{
pdata = pdata;
OS_ENTER_CRITICAL();
PC_VectSet(0x08, OSTickISR); //安装时钟中断向量
PC_SetTickRate(OS_TICKS_PER_SEC); //设置uCOS_II时钟频率
OS_EXIT_CRITICAL();
OSStatInit( ); //初始化统计任务
OSTaskCreate(MyTask, //创建任务YouTask
0, //给任务传递参数
&MyTaskStk[TASK_STK_SIZE - 1], //设置任务堆栈栈顶指针
6); //使任务的优先级别为6
OSTaskCreate(YouTask, //创建任务YouTask
0, //给任务传递参数
&YouTaskStk[TASK_STK_SIZE - 1], //设置任务堆栈栈顶指针
7); //使任务的优先级别为7
OSTaskCreate(HerTask, //创建任务YouTask
0, //给任务传递参数
&HerTaskStk[TASK_STK_SIZE - 1], //设置任务堆栈栈顶指针
8);
OSTaskSuspend(OS_PRIO_SELF); //把StartTask给挂起来了
}
/******任务MyTask**********/
void MyTask (void *pdata)
{
pdata = pdata;
for (;;)
{
OSTimeDlyHMSM(0,0,0,200);
{
PC_DispStr(10,++y,ss,DISP_BGND_BLACK+DISP_FGND_WHITE);
OSMutexPend(Semp,0,&err); PC_DispStr(10,++y,s1,DISP_BGND_BLACK+DISP_FGND_WHITE);
OSMutexPost(Semp);//发送信号量
QuitKey();
}
OSTimeDlyHMSM(0,0,0,200);
}
}
/************任务YouTask*************/
void YouTask (void *pdata)
{
pdata = pdata;
for (;;)
{
PC_DispStr(10,++y,s2,DISP_BGND_BLACK+DISP_FGND_WHITE);
QuitKey();
OSTimeDlyHMSM(0,0,0,300); //等待300ms
}
}
/************任务HerTaskk*******************/
void HerTask (void *pdata)
{
pdata = pdata;
for (;;)
{
OSMutexPend(Semp,0,&err);//请求信号量
PC_DispStr(10,++y,s3,DISP_BGND_BLACK+DISP_FGND_WHITE);//显示s3,表示请求成功
for(Times=0;Times<40000000;Times++)
{OS_Sched();}
OSMutexPost(Semp);//占用较长的时间,这里才释放互斥型信号量
QuitKey();
OSTimeDlyHMSM(0,0,1,0);
}
}
/*******************按键退出************************/
void QuitKey(void)
{
//如果按下Esc键则退出uCOS_II
if (PC_GetKey(&key) == TRUE)
{
if (key == 0x1B)
{
PC_DOSReturn( );//恢复DOS环境
}
}
}
运行结果:
看运行结果,先出现MyTask后出现YouTask ,正如我们所期望的,消除了优先级反转。
但是,出于好奇,我在HerTask任务里面加了一句。如下面代码所示。
for (;;)
{
OSMutexPend(Semp,0,&err);
PC_DispStr(10,++y,s3
,DISP_BGND_BLACK+DISP_FGND_WHITE);
for(Times=0;Times<40000000;Times++)
{OS_Sched();}
OSMutexPost(Semp);
//加了下面这一句,通过显示HerTask over来确认HerTask什么时候释放了信号量
PC_DispStr(10,++y,"HerTask over",
DISP_BGND_BLACK+DISP_FGND_WHITE);
QuitKey();
OSTimeDlyHMSM(0,0,1,0);
}
而运行结果让我惊讶。
乍一看,怎么HerTask没有释放信号量,MyTask就已经得到互斥信号量并且运行了呢,YouTask也运行了。这不是和理论相违背吗。按照常理,不是应该HerTask先释放信号量,然后MyTask才能请求,运行吗?
先别忙着质疑理论,为了弄清楚哪里出错了,我又在HerTask释放信号量的语句前,加了一句显示的语句,
for (;;)
{
OSMutexPend(Semp,0,&err); PC_DispStr(10,++y,s3,DISP_BGND_BLACK+DISP_FGND_WHITE);
for(Times=0;Times<40000000;Times++)
{OS_Sched();}
//这一次在释放前加了一句显示HerTask over1
PC_DispStr(10,++y,"HerTask over1",
DISP_BGND_BLACK+DISP_FGND_WHITE);
OSMutexPost(Semp);
PC_DispStr(10,++y,"HerTask over",
DISP_BGND_BLACK+DISP_FGND_WHITE);
QuitKey();
OSTimeDlyHMSM(0,0,1,0);
}
结果如下:
说明MyTask获得信号量是在HerTask任务的语句:for(Times=0;Times<40000000;Times++){OS_Sched();}之后才获得信号量的,准确来说是在显示了字符串”HerTask over1”之后才获得信号量的。
所以,只能说明在显示完”HerTask over1”之后,任务进行了切换,而且切换发生在了显示”HerTask over”之前,那么也就是说在OSMutexPost()函数里面肯定进行了切换。这是唯一的可能了,因为没有设置中断切换,那么要从HerTask转到MyTask执行,就只剩下任务级切换了。所以我查了一下OSMutexPost()函数的定义,发现函数里面确实调用了OSched()进行了任务的切换,所以说此次切换就是使得MyTask得以执行。所以以上的疑问也就解开了。
以上就是我对于互斥型信号量的一些理解,不妥之处欢迎指正。