直观理解uCOSII中的信号量的作用、优先级翻转现象、互斥信号量对优先级翻转现象的作用

  1. 信号量的作用

  2. 优先级翻转现象

  3. uCOS中的特殊信号量——互斥信号量


本文作为一个学习uCOS的经验分享,希望能给初学小白们一个参考。以例程和运行效果来说明,对一些概念性的东西这里不做过多解释,网上相关文章多如牛毛。本文中的实验需要一些uCOS相关的前置知识,比如OS初始化,任务创建等。

测试前准备:

1、串口一个,当做可被抢占的资源。

2、准备好三个用户任务,设置优先级为高、中、低(具体值自己设置,比如5、10、15),按照优先级命名函数。

3、创建一个信号量。

创建信号量的代码:

OS_EVENT *testsem;

testsem = OSSemCreate(1);

任务代码分别为:

void TaskHigh(void *p_arg)
{
    u8 err;
    
    while(1)
    {
        OSSemPend(testsem, 0, &err); //申请(等待)信号量

        printf("%c",'T');delay_ms(10);printf("%c",'a');delay_ms(10);
        printf("%c",'s');delay_ms(10);printf("%c",'k');delay_ms(10);
        printf("%c",'H');delay_ms(10);printf("%c",'i');delay_ms(10);
        printf("%c",'g');delay_ms(10);printf("%c",'h');delay_ms(10);
        printf("%c",' ');delay_ms(10);printf("%c",'g');delay_ms(10);
        printf("%c",'e');delay_ms(10);printf("%c",'t');delay_ms(10);
        printf("%c",' ');delay_ms(10);printf("%c",'t');delay_ms(10);
        printf("%c",'h');delay_ms(10);printf("%c",'e');delay_ms(10);
        printf("%c",' ');delay_ms(10);printf("%c",'c');delay_ms(10);
        printf("%c",'o');delay_ms(10);
        printf("%c",'m');printf("%c",'\r');printf("%c",'\n');
        
        OSSemPost(testsem);
        OSTimeDly(1);
    }
}

void TaskMedium(void *pdata)
{
    while(1)
    {
        printf("_\r\n");

        OSTimeDly(10);
    }
}
void TaskLow(void *p_arg)
{
    u8 err;
    
    while(1)
    {
        OSSemPend(testsem, 0, &err); //申请(等待)信号量,不设置超时。


        printf("%c",'T');delay_ms(10);printf("%c",'a');delay_ms(10);
        printf("%c",'s');delay_ms(10);printf("%c",'k');delay_ms(10);
        printf("%c",'L');delay_ms(10);printf("%c",'o');delay_ms(10);
        printf("%c",'w');delay_ms(10);printf("%c",' ');delay_ms(10);
        printf("%c",'g');delay_ms(10);printf("%c",'e');delay_ms(10);
        printf("%c",'t');delay_ms(10);printf("%c",' ');delay_ms(10);
        printf("%c",'t');delay_ms(10);printf("%c",'h');delay_ms(10);
        printf("%c",'e');delay_ms(10);printf("%c",' ');delay_ms(10);
        printf("%c",'c');delay_ms(10);printf("%c",'o');delay_ms(10);
        printf("%c",'m');printf("%c",'\r');printf("%c",'\n');
                
        OSSemPost(testsem); //释放信号量
        OSTimeDly(1);
    }
}

说明一下这么设计的意图,为了更好观察相关的现象,选择串口作为公共资源比设置一个变量数组更加直观,被抢占后能直接打印当前任务的字符串。代码中使用了阻塞延时是为了延迟任务的执行速度,另一个角度说就是能更明显的看到被抢占的现象。


  • 信号量的作用---------------------------------

简单归纳为,对于一些资源,在使用时可能会被打扰或被抢占,而我又不想被抢占,这时就需要有个规则或约定来防止这种事情发生,这个规则或约定就是信号量。

直接进入正题。

  • 先看看没信号量情况下的现象。

测试条件:不创建TaskMedium,只保留TaskLow、TaskHigh,在TaskLow、TaskHigh 中屏蔽OSSemPend() 和OSSemPost()。

运行效果如下:

红圈多出来的一个字符就是TaskLow任务需要打印的字符串。由此可知TaskLow任务在使用串口时,被优先级高的TaskHigh给抢去了。如果不想被抢走就需要用到信号量。

  • 接下来看看使用信号量后的现象。

测试条件:不创建TaskMedium,只保留TaskLow、TaskHigh。在TaskLow、TaskHigh 中使用OSSemPend() 和OSSemPost()。

运行效果如下:

使用信号量后, TaskLow任务能完整打印字符串,说明在使用串口时不会被抢走了。

需要注意的是信号量可以分为二值信号量和计数信号量。计数信号量可根据资源数量设置信号量的数量,举个栗子比如银行业务窗口只能同时有5个人办理业务,再多的就要排队,这时就设置信号量数量为5,来一个人办理信号量就减一,减到0时说明人满了,其他人就要排队。当有人办理完时,信号量就加一,队伍里的人就可以出来一个去办理,这时信号量又减一,以此类推。计数信号量目前我还没使用过,不多讨论。

而二值信号量,数量只有1(0和1两个值),其中一个功能就是互斥。什么叫互斥,就是你用了我就不能用,我用了你就不能用,上面例程中使用的就是二值信号量,体现了互斥功能。二值信号量还有个功能就是同步,简单说就是当做一个标志量来使用,即一个地方只释放信号量,另一个地方等到信号量后就能执行。

  • 优先级翻转现象---------------------------------

在uCOSII中,每个任务都有一个唯一的优先级。一句话概括优先级翻转现象,爸爸都还没出声,就轮到儿子大声说话了。

  • 使用二值信号量出现的优先级翻转现象。

 测试条件:创建所有任务。在TaskLow、TaskHigh 中使用OSSemPend() 和OSSemPost()。

运行结果如下:

小横杠是TaskMedium任务打印的字符串,可以看到TaskLow任务不停被打断。

这么看可能看不出什么名堂,低优先级被高优先级打断不是很正常吗?哪里能看到优先级翻转了呢?结合下图继续往下看。

有时候,低优先级的任务在占着资源未释放信号量,高优先级的任务也要用资源的话就得等待(比如TaskLow任务正在占用串口打印字符串,TaskHigh任务在等待),这时候有个中优先级的任务需要需要运行(比如TaskMedium任务过来打印小横杠)。因为TaskHigh优先级高,按理说TaskHigh任务只是在等待CPU使用权(使用了OSTimeDly()之类叫主动释放,这时谁要用CPU都不关心了),并不允许其他任务(优先级比TaskHigh低的)比它先得到使用权。就像排队,下一个就到我了,不许有人插队进来。

但实际就偏有人插队进来。如运行效果,TaskLow任务不断被打扰,但由于还未释放信号量,TaskHigh任务是没法获得CPU使用权来运行的,只能干瞪眼看着可恶的TaskMedium任务在捣乱,这看起来是TaskMedium优先级比TaskHigh高了,这就是优先级翻转现象。TaskMedium任务捣乱会让TaskLow任务不能尽快完成,TaskHigh任务就需要花更多时间等待。如果TaskMedium任务本身就需要很多时间来运行,TaskHigh任务将等到天荒地老,这对于实时操作系统是个灾难。

 这里需要说明一下,TaskMedium任务不通过信号量使用了同一个串口,原则上违背了例程“使用信号量达到资源独占”的目的,但为了能体现优先级反转现象才这么设计,TaskMedium可以不用串口,去做别的事,但依旧会因优先级高于TaskLow而打断TaskLow。

  •  uCOS中的特殊信号量——互斥信号量

 互斥信号量是一种二值信号量,在uCOS中能解决优先级翻转的问题。

  •  使用互斥信号量解决优先级翻转问题。

测试条件:创建所有任务创建一个互斥信号量。将TaskLow、TaskHigh 中OSSemPend() 和OSSemPost()替换成OSMutexPend() OSMutexPost()

创建互斥信号量的代码:

OS_EVENT *testmutex;

testmutex = OSMutexCreate(MUTEX_PRIO, &err);  //MUTEX_PRIO是占用互斥信号量的任务被提升时所需的优先级

 运行结果如下:

 可以看到TaskLow任务已经能完整打印字符串。

互斥信号量能解决优先级翻转问题的关键在于,把占用信号量任务的优先级给提高了。TaskLow任务在占用信号量期间,优先级会被提升,这样就不会被打断以便尽快运行完释放信号量,释放信号量后TaskLow任务优先级会恢复到原来的。

使用互斥信号量需要注意一些地方。

官方明确规定,任务占用互斥信号量期间(还未释放信号量),不能挂起该任务,也就是不能使用延时之类的将任务挂起。否则这互斥信号量就失效了。

对于提升优先级到底需要提升到多少,这个根据情况而定了,一般比所有等待互斥信号量的任务高一级就行,比如TaskHigh 优先级5,可以把TaskLow优先级提升至4。

还有优先级天花板的问题,即等待互斥信号的任务是最高了(0级),占用信号量的任务还能不能提升?此问题网上也有很多讨论,这里不多赘述。

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值