互斥型信号量

在任哲的书本《嵌入式实时操作系统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得以执行。所以以上的疑问也就解开了。
以上就是我对于互斥型信号量的一些理解,不妥之处欢迎指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值