UCOSII学习之路3 -任务同步之互斥

本文详细介绍了UCOSII中互斥信号量的概念和作用,区别于普通信号量,互斥信号量主要用于资源的互斥访问,防止优先级反转问题。文章通过代码解析了互斥信号量的创建、获取、删除和查询过程,揭示了其内部的工作机制,包括优先级提升和资源管理等关键点。
摘要由CSDN通过智能技术生成

上一节我们引入了信号量的概念,这一讲我们将揭晓互斥信号量的奥秘。

互斥信号量和信号量虽然都带了信号量的帽子,但是二者却有着不同的运用场合,互斥信号量相比而言经常用于一些资源的互斥访问,比如打印机、厕所等,这里的厕所指的是单厕,哈哈哈。这样有的人就要问了,那信号量设置起始cnt为1不也可以实现资源的互斥访问吗,这样的话我们直接使用信号量的实现不就可以了吗?答案当然是否认的,作为OS的开发者,哪些大咖们怎么可能没有想到这些问题。-----为了解决在信号量使用过程中一些任务的优先级反转问题(就是低优先级抢占CPU在高优先级任务前得到运行的现象),于是乎给信号量来了一个优先级提升,但凡低优先级任务抢占CPU后,如果后面来了一个高优先级任务申请该"信号量",那么低优先级任务将被提升到所有任务中最高优先级来保证"信号量"可以尽早的得到释放。然后就成了我们今天要谈的互斥信号量。


首先来看看互斥信号量在事件控制块的基础上使用了哪些资源。


从图中 可以看出,OSEventType用于指示资源的类型,而对于,其实OSEventCnt的高八位用于存放提升后的优先级,而前面则用于指示资源的状态,以及在优先级提升后短暂的存放开始的任务优先级,后两个OSEventGrp和OSEventTbl[]在任何一种类型的等待事件过程中都会使用,我们不讲,那我们就来说说OSEventPtr,很多人都任务在互斥里边他没有被使用,直接指向NULL,其实不然,他其实指向了任务的TCB。


互斥信号量的操作一共6个函数
OS_EVENT  *OSMutexCreate(INT8U  prio,  INT8U *perr); //互斥信号量的创建
void  OSMutexPend (OS_EVENT *pevent, INT32U timeout,  INT8U  *perr);//挂起
INT8U  OSMutexPost (OS_EVENT *pevent);//释放
BOOLEAN  OSMutexAccept (OS_EVENT  *pevent, INT8U     *perr);//无等待挂起
OS_EVENT  *OSMutexDel (OS_EVENT  *pevent, INT8U  opt,  INT8U     *perr);//删除互斥量
INT8U  OSMutexQuery (OS_EVENT  *pevent,  OS_MUTEX_DATA  *p_mutex_data);//查询

一个内部函数
static  void  OSMutex_RdyAtPrio (OS_TCB  *ptcb, INT8U    prio);

其通讯机理如下图



好了  相信大家对互斥信号量有了一个初步的认识。那我们继续

       与以往相同,我们来分析分析这些函数的内部实现

[cpp]  view plain   copy
  1. OS_EVENT  *OSMutexCreate (INT8U   prio,  
  2.                          INT8U  *perr)  
  3. {  
  4.     OS_EVENT  *pevent;  
  5. #if OS_CRITICAL_METHOD == 3u                               /* Allocate storage for CPU status register */  
  6.     OS_CPU_SR  cpu_sr = 0u;  
  7. #endif  
  8.   
  9. #ifdef OS_SAFETY_CRITICAL  
  10.     if (perr == (INT8U *)0) {  
  11.         OS_SAFETY_CRITICAL_EXCEPTION();  
  12.         return ((OS_EVENT *)0);  
  13.     }  
  14. #endif  
  15.   
  16. #ifdef OS_SAFETY_CRITICAL_IEC61508  
  17.     if (OSSafetyCriticalStartFlag == OS_TRUE) {  
  18.         OS_SAFETY_CRITICAL_EXCEPTION();  
  19.         return ((OS_EVENT *)0);  
  20.     }  
  21. #endif  
  22.   
  23. #if OS_ARG_CHK_EN > 0u  
  24.     if (prio != OS_PRIO_MUTEX_CEIL_DIS) { //Prio安全范围检测,其可通过OS_PRIO_MUTEX_CEIL_DIS来失能  
  25.         if (prio >= OS_LOWEST_PRIO) {                      /* Validate PCP                             */  
  26.            *perr = OS_ERR_PRIO_INVALID;  
  27.             return ((OS_EVENT *)0);  
  28.         }  
  29.     }  
  30. #endif  
  31.     if (OSIntNesting > 0u) {                               /* See if called from ISR ...               */  
  32.         *perr = OS_ERR_CREATE_ISR;                         /* ... can't CREATE mutex from an ISR       */  
  33.         return ((OS_EVENT *)0);  
  34.     }  
  35.     OS_ENTER_CRITICAL();  
  36.     if (prio != OS_PRIO_MUTEX_CEIL_DIS) { //  
  37.         if (OSTCBPrioTbl[prio] != (OS_TCB *)0) {        //检查提升的优先级是否占用  
  38.             OS_EXIT_CRITICAL();                            /* Task already exist at priority ...       */  
  39.            *perr = OS_ERR_PRIO_EXIST;       
  40.             return ((OS_EVENT *)0);  
  41.         }  
  42.         OSTCBPrioTbl[prio] = OS_TCB_RESERVED;              //没有被占用 将保留                 */  
  43.     }  
  44.   
  45.     pevent = OSEventFreeList;                              /* Get next free event control block        */  
  46.     if (pevent == (OS_EVENT *)0) {            //事件控制块资源耗尽              
  47.         if (prio != OS_PRIO_MUTEX_CEIL_DIS) { //  
  48.             OSTCBPrioTbl[prio] = (OS_TCB *)0;      //释放占用的TCB优先级资源  
  49.         }  
  50.         OS_EXIT_CRITICAL();  
  51.        *perr = OS_ERR_PEVENT_NULL;                         /* No more event control blocks             */  
  52.         return (pevent);  
  53.     }  
  54.     OSEventFreeList     = (OS_EVENT *)OSEventFreeList->OSEventPtr; /* Adjust the free list             */  
  55.     OS_EXIT_CRITICAL();  
  56.     pevent->OSEventType = OS_EVENT_TYPE_MUTEX;  
  57.     pevent->OSEventCnt  = (INT16U)((INT16U)prio << 8u) | OS_MUTEX_AVAILABLE;// 将提升的优先级放到cnt的高八位  
  58.     pevent->OSEventPtr  = (void *)0;                       /* No task owning the mutex                 */  
  59. #if OS_EVENT_NAME_EN > 0u  
  60.     pevent->OSEventName = (INT8U *)(void *)"?";  
  61. #endif  
  62.     OS_EventWaitListInit(pevent);//初始化事件等待list  
  63.    *perr = OS_ERR_NONE;  
  64.     return (pevent);  
  65. }  
该函数的目的是初始化事件控制块为互斥信号量,其中,当prio输入为OS_PRIO_MUTEX_CEIL_DIS,任务将不进行优先级提升,此时和单一的信号量的作用是相同的。还有一点也是需要特别留意,如果任务的优先级提升了,那么任务在使用完指定资源过后,怎么重新恢复他原有的优先级呢?想必看过源码的都已经了解了,这也是UCOS设计者的另外一个高明之处---变量的复用,通过将提升后的任务优先级放到OSEventCnt的高八位,从而保存该优先级而不需要重新定义变量,节省了系统对RAM的要求。

[cpp]  view plain   copy
  1. void  OSMutexPend (OS_EVENT  *pevent,  
  2.                    INT32U     timeout,  
  3.                    INT8U     *perr)  
  4. {  
  5.     INT8U      pcp;                                        /* Priority Ceiling Priority (PCP)          */  
  6.     INT8U      mprio;                                      /* Mutex owner priority                     */  
  7.     BOOLEAN    rdy;                                        /* Flag indicating task was ready           */  
  8.     OS_TCB    *ptcb;  
  9.     OS_EVENT  *pevent2;  
  10.     INT8U      y;  
  11. #if OS_CRITICAL_METHOD == 3u                               /* Allocate storage for CPU status register */  
  12.     OS_CPU_SR  cpu_sr = 0u;  
  13. #endif  
  14.   
  15.   
  16.   
  17. #ifdef OS_SAFETY_CRITICAL  
  18.     if (perr == (INT8U *)0) {  
  19.         OS_SAFETY_CRITICAL_EXCEPTION();  
  20.         return;  
  21.     }  
  22. #endif  
  23.   
  24. #if OS_ARG_CHK_EN > 0u  
  25.     if (pevent == (OS_EVENT *)0) {                         /* Validate 'pevent'                        */  
  26.         *perr = OS_ERR_PEVENT_NULL;  
  27.         return;  
  28.     }  
  29. #endif  
  30.     if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {      /* Validate event block type                */  
  31.         *perr = OS_ERR_EVENT_TYPE;  
  32.         return;  
  33.     }  
  34.     if (OSIntNesting > 0u) {                               /* See if called from ISR ...               */  
  35.         *perr = OS_ERR_PEND_ISR;                           /* ... can't PEND from an ISR               */  
  36.         return;  
  37.     }  
  38.     if (OSLockNesting > 0u) {                              /* See if called with scheduler locked ...  */  
  39.         *perr = OS_ERR_PEND_LOCKED;                        /* ... can't PEND when locked               */  
  40.         return;  
  41.     }  
  42. /*$PAGE*/       //前面全部是一些参数的范围检查之类的我们不做解释  
  43.     OS_ENTER_CRITICAL();  
  44.     pcp = (INT8U)(pevent->OSEventCnt >> 8u);               /* Get PCP from mutex                       */  
  45.                                                            /* Is Mutex available?                      */  
  46.     if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) { //表明当前资源未被占用  
  47.         pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;       //设置资源被占用  
  48.         pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;         //将原优先级保存到cnt低八位  
  49.         pevent->OSEventPtr  = (void *)OSTCBCur;           //指向提升前的TCB块  
  50.         if ((pcp != OS_PRIO_MUTEX_CEIL_DIS) &&  
  51.             (OSTCBCur->OSTCBPrio <= pcp)) {                //进入表示允许优先级提升  
  52.              OS_EXIT_CRITICAL();                           /*      ... than current task!              */  
  53.             *perr = OS_ERR_PCP_LOWER;  //提升优先级失败,原因在于提升后的优先级<当前优先级  
  54.         } else {  
  55.              OS_EXIT_CRITICAL();  
  56.             *perr = OS_ERR_NONE;  
  57.         }  
  58.         return;  
  59.     } //以上为资源没有被占用的情况  
  60.     if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {  
  61.         mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); //获取原优先级  
  62.         ptcb  = (OS_TCB *)(pevent->OSEventPtr);                   /*     Point to TCB of mutex owner   */  
  63.         if (ptcb->OSTCBPrio > pcp) {                             //当提升优先级高于原优先级  
  64.             if (mprio > OSTCBCur->OSTCBPrio) { //同时当前申请互斥资源的任务优先级高于原优先级,将实现优先级提升  
  65.                 y = ptcb->OSTCBY; //获取以前优先级的组号  
  66.                 if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != 0u) {      //存在就绪任务  
  67.                     OSRdyTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX;     /*     Yes, Remove owner from Rdy ...*/  
  68.                     if (OSRdyTbl[y] == 0u) {                      /*          ... list at current prio */  
  69.                         OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;  
  70.                     }  
  71.                     rdy = OS_TRUE; //清除就绪标志 并设立标志  
  72.                 } else {  
  73.                     pevent2 = ptcb->OSTCBEventPtr;  
  74.                     if (pevent2 != (OS_EVENT *)0) {               /* Remove from event wait list       */  
  75.                         y = ptcb->OSTCBY;  
  76.                         pevent2->OSEventTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX;  
  77.                         if (pevent2->OSEventTbl[y] == 0u) {  
  78.                             pevent2->OSEventGrp &= (OS_PRIO)~ptcb->OSTCBBitY;  
  79.                         }  
  80.                     }  
  81.                     rdy = OS_FALSE;                        /* No                                       */  
  82.                 }  
  83.                 ptcb->OSTCBPrio = pcp;                     //提升原优先级为pcp        
  84. #if OS_LOWEST_PRIO <= 63u  
  85.                 ptcb->OSTCBY    = (INT8U)( ptcb->OSTCBPrio >> 3u);  
  86.                 ptcb->OSTCBX    = (INT8U)( ptcb->OSTCBPrio & 0x07u);  
  87. #else  
  88.                 ptcb->OSTCBY    = (INT8U)((INT8U)(ptcb->OSTCBPrio >> 4u) & 0xFFu);  
  89.                 ptcb->OSTCBX    = (INT8U)( ptcb->OSTCBPrio & 0x0Fu);  
  90. #endif  
  91.                 ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY);  
  92.                 ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX);  
  93.   
  94.                 if (rdy == OS_TRUE) {                     //完成未完成前任务的就绪状态  
  95.                     OSRdyGrp               |= ptcb->OSTCBBitY; /* ... make it ready at new priority.   */  
  96.                     OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;  
  97.                 } else {  
  98.                     pevent2 = ptcb->OSTCBEventPtr;  
  99.                     if (pevent2 != (OS_EVENT *)0) {        /* Add to event wait list                   */  
  100.                         pevent2->OSEventGrp               |= ptcb->OSTCBBitY;  
  101.                         pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;  
  102.                     }  
  103.                 }  
  104.                 OSTCBPrioTbl[pcp] = ptcb; //写入优先级组  
  105.             }  
  106.         }  
  107.     }  
  108.     OSTCBCur->OSTCBStat     |= OS_STAT_MUTEX;         /* Mutex not available, pend current task        */  
  109.     OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;  
  110.     OSTCBCur->OSTCBDly       = timeout;               /* Store timeout in current task's TCB           */  
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值