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

来源:互联网 发布:painter中文版mac 编辑:程序博客网 时间:2024/05/19 16:04

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

互斥信号量和信号量虽然都带了信号量的帽子,但是二者却有着不同的运用场合,互斥信号量相比而言经常用于一些资源的互斥访问,比如打印机、厕所等,这里的厕所指的是单厕,哈哈哈。这样有的人就要问了,那信号量设置起始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           */  
  111.     OS_EventTaskWait(pevent);                        //挂起本任务  
  112.     OS_EXIT_CRITICAL();  
  113.     OS_Sched();                                       //任务调度,当占有资源释放时执行以下内容  
  114.     OS_ENTER_CRITICAL();  
  115.     switch (OSTCBCur->OSTCBStatPend) {                /* See if we timed-out or aborted                */  
  116.         case OS_STAT_PEND_OK:  
  117.              *perr = OS_ERR_NONE;  
  118.              break;  
  119.   
  120.         case OS_STAT_PEND_ABORT:  
  121.              *perr = OS_ERR_PEND_ABORT;               /* Indicate that we aborted getting mutex        */  
  122.              break;  
  123.   
  124.         case OS_STAT_PEND_TO:  
  125.         default:  
  126.              OS_EventTaskRemove(OSTCBCur, pevent);  
  127.              *perr = OS_ERR_TIMEOUT;                  /* Indicate that we didn't get mutex within TO   */  
  128.              break;  
  129.     }  
  130.     OSTCBCur->OSTCBStat          =  OS_STAT_RDY;      //任务等待资源完成  
  131.     OSTCBCur->OSTCBStatPend      =  OS_STAT_PEND_OK;  /* Clear pend  status                            */  
  132.     OSTCBCur->OSTCBEventPtr      = (OS_EVENT  *)0;    /* Clear event pointers                          */  
  133. #if (OS_EVENT_MULTI_EN > 0u)  
  134.     OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;  
  135. #endif  
  136.     OS_EXIT_CRITICAL();  
  137. }  

本函数稍稍复杂一点,其实也比较简单,他的功能主要考虑四种情况:
1、不执行优先级提升时,资源不占用。
这种比较简单,也是执行时间最短的及最快的,任务直接抢占资源并返回。
2、不执行优先级提升,资源被占用。
这种也简单,如果是这种情况的话,互斥信号量操作和信号量完全一样。基本是等号。
3、执行提升时资源不占用
这种也是执行最快的,资源直接占用并返回。
4、提升时资源被占用
这个就是核心了,也是互斥信号量最难以理解的地方,他处理的情况也分为2种,
1>让资源被占用,后申请资源的任务优先级高于正在占用任务的优先级,执行优先级提升,因为这将会导致优先级反转,这也是互斥信号量为解决优先级反转而引入的概念。
2>当其优先级低于正在占用任务的优先级,类型不会被提升,操作如信号量

有的朋友可能会有这样的疑问,执行类型提升后,那么原来的优先级资源如何处理,其实UCOSII对其基本未处理,只是将该任务的Prio赋值到一个较高的水平,而原Prio此时将一直被占用,并同样保存了一个指向该任务TCB的指针。

[cpp] view plain copy
  1. BOOLEAN  OSMutexAccept (OS_EVENT  *pevent,  
  2.                         INT8U     *perr)  
  3. {  
  4.     INT8U      pcp;                                    /* Priority Ceiling Priority (PCP)              */  
  5. #if OS_CRITICAL_METHOD == 3u                           /* Allocate storage for CPU status register     */  
  6.     OS_CPU_SR  cpu_sr = 0u;  
  7. #endif  
  8.   
  9.   
  10.   
  11. #ifdef OS_SAFETY_CRITICAL  
  12.     if (perr == (INT8U *)0) {  
  13.         OS_SAFETY_CRITICAL_EXCEPTION();  
  14.         return (OS_FALSE);  
  15.     }  
  16. #endif  
  17.   
  18. #if OS_ARG_CHK_EN > 0u  
  19.     if (pevent == (OS_EVENT *)0) {                     /* Validate 'pevent'                            */  
  20.         *perr = OS_ERR_PEVENT_NULL;  
  21.         return (OS_FALSE);  
  22.     }  
  23. #endif  
  24.     if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {  /* Validate event block type                    */  
  25.         *perr = OS_ERR_EVENT_TYPE;  
  26.         return (OS_FALSE);  
  27.     }  
  28.     if (OSIntNesting > 0u) {                           /* Make sure it's not called from an ISR        */  
  29.         *perr = OS_ERR_PEND_ISR;  
  30.         return (OS_FALSE);  
  31.     }  
  32.     OS_ENTER_CRITICAL();                               /* Get value (0 or 1) of Mutex                  */  
  33.     pcp = (INT8U)(pevent->OSEventCnt >> 8u);           /* Get PCP from mutex                           */  
  34.     if ((pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) {  
  35.         pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;   /*      Mask off LSByte (Acquire Mutex)         */  
  36.         pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;     /*      Save current task priority in LSByte    */  
  37.         pevent->OSEventPtr  = (void *)OSTCBCur;        /*      Link TCB of task owning Mutex           */  
  38.         if ((pcp != OS_PRIO_MUTEX_CEIL_DIS) &&  
  39.             (OSTCBCur->OSTCBPrio <= pcp)) {            /*      PCP 'must' have a SMALLER prio ...      */  
  40.              OS_EXIT_CRITICAL();                       /*      ... than current task!                  */  
  41.             *perr = OS_ERR_PCP_LOWER;  
  42.         } else {  
  43.              OS_EXIT_CRITICAL();  
  44.             *perr = OS_ERR_NONE;  
  45.         }  
  46.         return (OS_TRUE);  
  47.     } //其实以上的内容和OSMutexPend都是一样的,我们不做即使  
  48.     OS_EXIT_CRITICAL();   
  49.     *perr = OS_ERR_NONE;  
  50.     return (OS_FALSE);  
  51. }  

相信看完OSMutexPend()函数的朋友再来理解该函数是不是感觉就太简单了,他们仅有的区别在于,一个执行完,如果资源被占用将被挂起,而另一个会直接返回。

[cpp] view plain copy
  1. INT8U  OSMutexPost (OS_EVENT *pevent)  
  2. {  
  3.     INT8U      pcp;                                   /* Priority ceiling priority                     */  
  4.     INT8U      prio;  
  5. #if OS_CRITICAL_METHOD == 3u                          /* Allocate storage for CPU status register      */  
  6.     OS_CPU_SR  cpu_sr = 0u;  
  7. #endif  
  8.   
  9.   
  10.   
  11.     if (OSIntNesting > 0u) {                          /* See if called from ISR ...                    */  
  12.         return (OS_ERR_POST_ISR);                     /* ... can't POST mutex from an ISR              */  
  13.     }  
  14. #if OS_ARG_CHK_EN > 0u  
  15.     if (pevent == (OS_EVENT *)0) {                    /* Validate 'pevent'                             */  
  16.         return (OS_ERR_PEVENT_NULL);  
  17.     }  
  18. #endif  
  19.     if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) { /* Validate event block type                     */  
  20.         return (OS_ERR_EVENT_TYPE);  
  21.     }  
  22.     OS_ENTER_CRITICAL();  
  23.     pcp  = (INT8U)(pevent->OSEventCnt >> 8u);    //     获取PCP优先级  
  24.     prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);  //获取原优先级  
  25.     if (OSTCBCur != (OS_TCB *)pevent->OSEventPtr) {   /* See if posting task owns the MUTEX            */  
  26.         OS_EXIT_CRITICAL();  
  27.         return (OS_ERR_NOT_MUTEX_OWNER);//互斥信号量错误  
  28.     }  
  29.     if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {  
  30.         if (OSTCBCur->OSTCBPrio == pcp) {             /* Did we have to raise current task's priority? */  
  31.             OSMutex_RdyAtPrio(OSTCBCur, prio);        //恢复原优先级  
  32.         }  
  33.         OSTCBPrioTbl[pcp] = OS_TCB_RESERVED;         //OS_TCB_RESERVED=1    
  34.     }  
  35.     if (pevent->OSEventGrp != 0u) {                   /* Any task waiting for the mutex?               */  
  36.                                                       /* Yes, Make HPT waiting for mutex ready         */  
  37.         prio                = OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);  
  38.         pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;  /*      Save priority of mutex's new owner       */  
  39.         pevent->OSEventCnt |= prio;  
  40.         pevent->OSEventPtr  = OSTCBPrioTbl[prio];     /*      Link to new mutex owner's OS_TCB         */  
  41.         if ((pcp  != OS_PRIO_MUTEX_CEIL_DIS) &&  
  42.             (prio <= pcp)) {                          /*      PCP 'must' have a SMALLER prio ...       */  
  43.             OS_EXIT_CRITICAL();                       /*      ... than current task!                   */  
  44.             OS_Sched();                               /*      Find highest priority task ready to run  */  
  45.             return (OS_ERR_PCP_LOWER);  
  46.         } else {  
  47.             OS_EXIT_CRITICAL();  
  48.             OS_Sched();                               /*      Find highest priority task ready to run  */  
  49.             return (OS_ERR_NONE);  
  50.         }  
  51.     }  
  52.     pevent->OSEventCnt |= OS_MUTEX_AVAILABLE;         /* No,  Mutex is now available                   */  
  53.     pevent->OSEventPtr  = (void *)0;  
  54.     OS_EXIT_CRITICAL();  
  55.     return (OS_ERR_NONE);  
  56. }  
  57. //以上主要是恢复原优先级,并并将占用的资源释放  
  58. OS_EVENT  *OSMutexDel (OS_EVENT  *pevent,  
  59.                        INT8U      opt,  
  60.                        INT8U     *perr)  
  61. {  
  62.     BOOLEAN    tasks_waiting;  
  63.     OS_EVENT  *pevent_return;  
  64.     INT8U      pcp;                                        /* Priority ceiling priority                */  
  65.     INT8U      prio;  
  66.     OS_TCB    *ptcb;  
  67. #if OS_CRITICAL_METHOD == 3u                               /* Allocate storage for CPU status register */  
  68.     OS_CPU_SR  cpu_sr = 0u;  
  69. #endif  
  70.   
  71.   
  72.   
  73. #ifdef OS_SAFETY_CRITICAL  
  74.     if (perr == (INT8U *)0) {  
  75.         OS_SAFETY_CRITICAL_EXCEPTION();  
  76.         return ((OS_EVENT *)0);  
  77.     }  
  78. #endif  
  79.   
  80. #if OS_ARG_CHK_EN > 0u  
  81.     if (pevent == (OS_EVENT *)0) {                         /* Validate 'pevent'                        */  
  82.         *perr = OS_ERR_PEVENT_NULL;  
  83.         return (pevent);  
  84.     }  
  85. #endif  
  86.     if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {      /* Validate event block type                */  
  87.         *perr = OS_ERR_EVENT_TYPE;  
  88.         return (pevent);  
  89.     }  
  90.     if (OSIntNesting > 0u) {                               /* See if called from ISR ...               */  
  91.         *perr = OS_ERR_DEL_ISR;                             /* ... can't DELETE from an ISR             */  
  92.         return (pevent);  
  93.     }  
  94.     OS_ENTER_CRITICAL();  
  95.     if (pevent->OSEventGrp != 0u) {                        /* See if any tasks waiting on mutex        */  
  96.         tasks_waiting = OS_TRUE;                           /* Yes                                      */  
  97.     } else {  
  98.         tasks_waiting = OS_FALSE;                          /* No                                       */  
  99.     }  
  100.     switch (opt) {  //opt为删除类型  
  101.         case OS_DEL_NO_PEND: //任务没有挂起的时候删除互斥信号        /* DELETE MUTEX ONLY IF NO TASK WAITING --- */  
  102.              if (tasks_waiting == OS_FALSE) {  
  103. #if OS_EVENT_NAME_EN > 0u  
  104.                  pevent->OSEventName   = (INT8U *)(void *)"?";  
  105. #endif  
  106.                  pcp                   = (INT8U)(pevent->OSEventCnt >> 8u);  
  107.                  if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {  
  108.                      OSTCBPrioTbl[pcp] = (OS_TCB *)0;    //释放占用的PCP提升资源          */  
  109.                  }  
  110.                  pevent->OSEventType   = OS_EVENT_TYPE_UNUSED;  
  111.                  pevent->OSEventPtr    = OSEventFreeList;  /* Return Event Control Block to free list  */  
  112.                  pevent->OSEventCnt    = 0u;  
  113.                  OSEventFreeList       = pevent;//重新将互斥量链接到空闲表  
  114.                  OS_EXIT_CRITICAL();  
  115.                  *perr                 = OS_ERR_NONE;  
  116.                  pevent_return         = (OS_EVENT *)0;    /* Mutex has been deleted                   */  
  117.              } else {  
  118.                  OS_EXIT_CRITICAL(); //存在将返回错误,此时不能删除  
  119.                  *perr                 = OS_ERR_TASK_WAITING;  
  120.                  pevent_return         = pevent;  
  121.              }  
  122.              break;  
  123.   
  124.         case OS_DEL_ALWAYS:       //总是删除互斥量                         /* ALWAYS DELETE THE MUTEX ---------------- */  
  125.              pcp  = (INT8U)(pevent->OSEventCnt >> 8u);                       /* Get PCP of mutex       */  
  126.              if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {  
  127.                  prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); /* Get owner's orig prio  */  
  128.                  ptcb = (OS_TCB *)pevent->OSEventPtr;  
  129.                  if (ptcb != (OS_TCB *)0) {                /* See if any task owns the mutex           */  
  130.                      if (ptcb->OSTCBPrio == pcp) {         /* See if original prio was changed         */  
  131.                          OSMutex_RdyAtPrio(ptcb, prio);    /* Yes, Restore the task's original prio    */  
  132.                      }  
  133.                  }  
  134.              }  
  135.              //别占用的资源强制释放  
  136.              while (pevent->OSEventGrp != 0u) {            /* Ready ALL tasks waiting for mutex        */  
  137.                  (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_ABORT);  
  138.              }  
  139. #if OS_EVENT_NAME_EN > 0u  
  140.              pevent->OSEventName   = (INT8U *)(void *)"?";  
  141. #endif  
  142.              pcp                   = (INT8U)(pevent->OSEventCnt >> 8u);  
  143.              if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {  
  144.                  OSTCBPrioTbl[pcp] = (OS_TCB *)0;          /* Free up the PCP                          */  
  145.              }  
  146.              pevent->OSEventType   = OS_EVENT_TYPE_UNUSED;  
  147.              pevent->OSEventPtr    = OSEventFreeList;      /* Return Event Control Block to free list  */  
  148.              pevent->OSEventCnt    = 0u;  
  149.              OSEventFreeList       = pevent;               /* Get next free event control block        */  
  150.              OS_EXIT_CRITICAL();  
  151.              if (tasks_waiting == OS_TRUE) {               /* Reschedule only if task(s) were waiting  */  
  152.                  OS_Sched();    //存在就绪任务将开展调度                           /* Find highest priority task ready to run  */  
  153.              }  
  154.              *perr         = OS_ERR_NONE;  
  155.              pevent_return = (OS_EVENT *)0;                /* Mutex has been deleted                   */  
  156.              break;  
  157.   
  158.         default:  
  159.              OS_EXIT_CRITICAL();  
  160.              *perr         = OS_ERR_INVALID_OPT; //无效opt输入  
  161.              pevent_return = pevent;  
  162.              break;  
  163.     }  
  164.     return (pevent_return); //返回删除的互斥量地址  
  165. }  

该任务主要是删除一个互斥量。
1、不占用任务删除类型:互斥量将在没有任务挂起时删除
2、总是删除类型:互斥量将总是删除任务,当存在任务挂起时将强制就绪释放

[cpp] view plain copy
  1. INT8U  OSMutexQuery (OS_EVENT       *pevent,  
  2.                      OS_MUTEX_DATA  *p_mutex_data)  
  3. {  
  4.     INT8U       i;  
  5.     OS_PRIO    *psrc;  
  6.     OS_PRIO    *pdest;  
  7. #if OS_CRITICAL_METHOD == 3u                     /* Allocate storage for CPU status register           */  
  8.     OS_CPU_SR   cpu_sr = 0u;  
  9. #endif  
  10.   
  11.     if (OSIntNesting > 0u) {                               /* See if called from ISR ...               */  
  12.         return (OS_ERR_QUERY_ISR);                         /* ... can't QUERY mutex from an ISR        */  
  13.     }  
  14. #if OS_ARG_CHK_EN > 0u  
  15.     if (pevent == (OS_EVENT *)0) {                         /* Validate 'pevent'                        */  
  16.         return (OS_ERR_PEVENT_NULL);  
  17.     }  
  18.     if (p_mutex_data == (OS_MUTEX_DATA *)0) {              /* Validate 'p_mutex_data'                  */  
  19.         return (OS_ERR_PDATA_NULL);  
  20.     }  
  21. #endif  
  22.     if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {      /* Validate event block type                */  
  23.         return (OS_ERR_EVENT_TYPE);  
  24.     }  
  25.     OS_ENTER_CRITICAL();  
  26.     p_mutex_data->OSMutexPCP  = (INT8U)(pevent->OSEventCnt >> 8u);  
  27.     p_mutex_data->OSOwnerPrio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);  
  28.     if (p_mutex_data->OSOwnerPrio == 0xFFu) {  
  29.         p_mutex_data->OSValue = OS_TRUE; //查看资源是否被占用  
  30.     } else {  
  31.         p_mutex_data->OSValue = OS_FALSE;  
  32.     }  
  33.     p_mutex_data->OSEventGrp  = pevent->OSEventGrp;        /* Copy wait list                           */  
  34.     psrc                      = &pevent->OSEventTbl[0];  
  35.     pdest                     = &p_mutex_data->OSEventTbl[0];  
  36.     for (i = 0u; i < OS_EVENT_TBL_SIZE; i++) { //执行数据拷贝  
  37.         *pdest++ = *psrc++;  
  38.     }  
  39.     OS_EXIT_CRITICAL();  
  40.     return (OS_ERR_NONE);  
  41. }  
  42. //该函数比较简单,直接拷贝pevent信息到p_mutex_data数据结构中。  
  43. static  void  OSMutex_RdyAtPrio (OS_TCB  *ptcb,  
  44.                                 INT8U    prio)  
  45. {  
  46.     INT8U  y;  
  47.   
  48.   
  49.     y            =  ptcb->OSTCBY;                          /* Remove owner from ready list at 'pcp'    */  
  50.     OSRdyTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX; //清除原就绪标志  
  51.     if (OSRdyTbl[y] == 0u) {  
  52.         OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;  
  53.     }  
  54.     ptcb->OSTCBPrio         = prio;  
  55.     OSPrioCur               = prio;                        /* The current task is now at this priority */  
  56. #if OS_LOWEST_PRIO <= 63u  
  57.     ptcb->OSTCBY            = (INT8U)((INT8U)(prio >> 3u) & 0x07u);  
  58.     ptcb->OSTCBX            = (INT8U)(prio & 0x07u);  
  59. #else  
  60.     ptcb->OSTCBY            = (INT8U)((INT8U)(prio >> 4u) & 0x0Fu);  
  61.     ptcb->OSTCBX            = (INT8U) (prio & 0x0Fu);  
  62. #endif  
  63.     ptcb->OSTCBBitY         = (OS_PRIO)(1uL << ptcb->OSTCBY); //在新的优先级上就绪任务  
  64.     ptcb->OSTCBBitX         = (OS_PRIO)(1uL << ptcb->OSTCBX);  
  65.     OSRdyGrp               |= ptcb->OSTCBBitY;             /* Make task ready at original priority     */  
  66.     OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;  
  67.     OSTCBPrioTbl[prio]      = ptcb;   
  68. }  

该函数主要是将一个任务优先级提升到prio层次,并将提升后的任务就绪。

到这里,互斥信号量的源码分析也就结束了,总结一点,互斥信号量和信号量其实大同小异,当然这是考虑信号量在只有一个时(及cnt=1)的情况,二者仅有的区别在于互斥信号量可以有效地防止优先级反转,而这在一些安全相关的系统中将是至关重要的。因此在处理对任务顺序较为敏感的任务的时候,需要使用互斥量来防止反转,增加系统的可预测性。
好了,互斥量就讲到这里
时间:170708  


[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           */  
  111.     OS_EventTaskWait(pevent);                        //挂起本任务  
  112.     OS_EXIT_CRITICAL();  
  113.     OS_Sched();                                       //任务调度,当占有资源释放时执行以下内容  
  114.     OS_ENTER_CRITICAL();  
  115.     switch (OSTCBCur->OSTCBStatPend) {                /* See if we timed-out or aborted                */  
  116.         case OS_STAT_PEND_OK:  
  117.              *perr = OS_ERR_NONE;  
  118.              break;  
  119.   
  120.         case OS_STAT_PEND_ABORT:  
  121.              *perr = OS_ERR_PEND_ABORT;               /* Indicate that we aborted getting mutex        */  
  122.              break;  
  123.   
  124.         case OS_STAT_PEND_TO:  
  125.         default:  
  126.              OS_EventTaskRemove(OSTCBCur, pevent);  
  127.              *perr = OS_ERR_TIMEOUT;                  /* Indicate that we didn't get mutex within TO   */  
  128.              break;  
  129.     }  
  130.     OSTCBCur->OSTCBStat          =  OS_STAT_RDY;      //任务等待资源完成  
  131.     OSTCBCur->OSTCBStatPend      =  OS_STAT_PEND_OK;  /* Clear pend  status                            */  
  132.     OSTCBCur->OSTCBEventPtr      = (OS_EVENT  *)0;    /* Clear event pointers                          */  
  133. #if (OS_EVENT_MULTI_EN > 0u)  
  134.     OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;  
  135. #endif  
  136.     OS_EXIT_CRITICAL();  
  137. }  

本函数稍稍复杂一点,其实也比较简单,他的功能主要考虑四种情况:
1、不执行优先级提升时,资源不占用。
这种比较简单,也是执行时间最短的及最快的,任务直接抢占资源并返回。
2、不执行优先级提升,资源被占用。
这种也简单,如果是这种情况的话,互斥信号量操作和信号量完全一样。基本是等号。
3、执行提升时资源不占用
这种也是执行最快的,资源直接占用并返回。
4、提升时资源被占用
这个就是核心了,也是互斥信号量最难以理解的地方,他处理的情况也分为2种,
1>让资源被占用,后申请资源的任务优先级高于正在占用任务的优先级,执行优先级提升,因为
这将会导致优先级反转,这也是互斥信号量为解决优先级反转而引入的概念。
2>当其优先级低于正在占用任务的优先级,类型不会被提升,操作如信号量

有的朋友可能会有这样的疑问,执行类型提升后,那么原来的优先级资源如何处理,其实UCOSII对其基本未处理,只是将该任务的Prio赋值到一个较高的水平,而原Prio此时将一直被占用,并同样保存了一个指向该任务TCB的指针。

[cpp] view plain copy
  1. BOOLEAN  OSMutexAccept (OS_EVENT  *pevent,  
  2.                         INT8U     *perr)  
  3. {  
  4.     INT8U      pcp;                                    /* Priority Ceiling Priority (PCP)              */  
  5. #if OS_CRITICAL_METHOD == 3u                           /* Allocate storage for CPU status register     */  
  6.     OS_CPU_SR  cpu_sr = 0u;  
  7. #endif  
  8.   
  9.   
  10.   
  11. #ifdef OS_SAFETY_CRITICAL  
  12.     if (perr == (INT8U *)0) {  
  13.         OS_SAFETY_CRITICAL_EXCEPTION();  
  14.         return (OS_FALSE);  
  15.     }  
  16. #endif  
  17.   
  18. #if OS_ARG_CHK_EN > 0u  
  19.     if (pevent == (OS_EVENT *)0) {                     /* Validate 'pevent'                            */  
  20.         *perr = OS_ERR_PEVENT_NULL;  
  21.         return (OS_FALSE);  
  22.     }  
  23. #endif  
  24.     if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {  /* Validate event block type                    */  
  25.         *perr = OS_ERR_EVENT_TYPE;  
  26.         return (OS_FALSE);  
  27.     }  
  28.     if (OSIntNesting > 0u) {                           /* Make sure it's not called from an ISR        */  
  29.         *perr = OS_ERR_PEND_ISR;  
  30.         return (OS_FALSE);  
  31.     }  
  32.     OS_ENTER_CRITICAL();                               /* Get value (0 or 1) of Mutex                  */  
  33.     pcp = (INT8U)(pevent->OSEventCnt >> 8u);           /* Get PCP from mutex                           */  
  34.     if ((pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) {  
  35.         pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;   /*      Mask off LSByte (Acquire Mutex)         */  
  36.         pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;     /*      Save current task priority in LSByte    */  
  37.         pevent->OSEventPtr  = (void *)OSTCBCur;        /*      Link TCB of task owning Mutex           */  
  38.         if ((pcp != OS_PRIO_MUTEX_CEIL_DIS) &&  
  39.             (OSTCBCur->OSTCBPrio <= pcp)) {            /*      PCP 'must' have a SMALLER prio ...      */  
  40.              OS_EXIT_CRITICAL();                       /*      ... than current task!                  */  
  41.             *perr = OS_ERR_PCP_LOWER;  
  42.         } else {  
  43.              OS_EXIT_CRITICAL();  
  44.             *perr = OS_ERR_NONE;  
  45.         }  
  46.         return (OS_TRUE);  
  47.     } //其实以上的内容和OSMutexPend都是一样的,我们不做即使  
  48.     OS_EXIT_CRITICAL();   
  49.     *perr = OS_ERR_NONE;  
  50.     return (OS_FALSE);  
  51. }  

相信看完OSMutexPend()函数的朋友再来理解该函数是不是感觉就太简单了,他们仅有的区别在于,一个执行完,如果资源被占用将被挂起,而另一个会直接返回。

[cpp] view plain copy
  1. INT8U  OSMutexPost (OS_EVENT *pevent)  
  2. {  
  3.     INT8U      pcp;                                   /* Priority ceiling priority                     */  
  4.     INT8U      prio;  
  5. #if OS_CRITICAL_METHOD == 3u                          /* Allocate storage for CPU status register      */  
  6.     OS_CPU_SR  cpu_sr = 0u;  
  7. #endif  
  8.   
  9.   
  10.   
  11.     if (OSIntNesting > 0u) {                          /* See if called from ISR ...                    */  
  12.         return (OS_ERR_POST_ISR);                     /* ... can't POST mutex from an ISR              */  
  13.     }  
  14. #if OS_ARG_CHK_EN > 0u  
  15.     if (pevent == (OS_EVENT *)0) {                    /* Validate 'pevent'                             */  
  16.         return (OS_ERR_PEVENT_NULL);  
  17.     }  
  18. #endif  
  19.     if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) { /* Validate event block type                     */  
  20.         return (OS_ERR_EVENT_TYPE);  
  21.     }  
  22.     OS_ENTER_CRITICAL();  
  23.     pcp  = (INT8U)(pevent->OSEventCnt >> 8u);    //     获取PCP优先级  
  24.     prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);  //获取原优先级  
  25.     if (OSTCBCur != (OS_TCB *)pevent->OSEventPtr) {   /* See if posting task owns the MUTEX            */  
  26.         OS_EXIT_CRITICAL();  
  27.         return (OS_ERR_NOT_MUTEX_OWNER);//互斥信号量错误  
  28.     }  
  29.     if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {  
  30.         if (OSTCBCur->OSTCBPrio == pcp) {             /* Did we have to raise current task's priority? */  
  31.             OSMutex_RdyAtPrio(OSTCBCur, prio);        //恢复原优先级  
  32.         }  
  33.         OSTCBPrioTbl[pcp] = OS_TCB_RESERVED;         //OS_TCB_RESERVED=1    
  34.     }  
  35.     if (pevent->OSEventGrp != 0u) {                   /* Any task waiting for the mutex?               */  
  36.                                                       /* Yes, Make HPT waiting for mutex ready         */  
  37.         prio                = OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);  
  38.         pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;  /*      Save priority of mutex's new owner       */  
  39.         pevent->OSEventCnt |= prio;  
  40.         pevent->OSEventPtr  = OSTCBPrioTbl[prio];     /*      Link to new mutex owner's OS_TCB         */  
  41.         if ((pcp  != OS_PRIO_MUTEX_CEIL_DIS) &&  
  42.             (prio <= pcp)) {                          /*      PCP 'must' have a SMALLER prio ...       */  
  43.             OS_EXIT_CRITICAL();                       /*      ... than current task!                   */  
  44.             OS_Sched();                               /*      Find highest priority task ready to run  */  
  45.             return (OS_ERR_PCP_LOWER);  
  46.         } else {  
  47.             OS_EXIT_CRITICAL();  
  48.             OS_Sched();                               /*      Find highest priority task ready to run  */  
  49.             return (OS_ERR_NONE);  
  50.         }  
  51.     }  
  52.     pevent->OSEventCnt |= OS_MUTEX_AVAILABLE;         /* No,  Mutex is now available                   */  
  53.     pevent->OSEventPtr  = (void *)0;  
  54.     OS_EXIT_CRITICAL();  
  55.     return (OS_ERR_NONE);  
  56. }  
  57. //以上主要是恢复原优先级,并并将占用的资源释放  
  58. OS_EVENT  *OSMutexDel (OS_EVENT  *pevent,  
  59.                        INT8U      opt,  
  60.                        INT8U     *perr)  
  61. {  
  62.     BOOLEAN    tasks_waiting;  
  63.     OS_EVENT  *pevent_return;  
  64.     INT8U      pcp;                                        /* Priority ceiling priority                */  
  65.     INT8U      prio;  
  66.     OS_TCB    *ptcb;  
  67. #if OS_CRITICAL_METHOD == 3u                               /* Allocate storage for CPU status register */  
  68.     OS_CPU_SR  cpu_sr = 0u;  
  69. #endif  
  70.   
  71.   
  72.   
  73. #ifdef OS_SAFETY_CRITICAL  
  74.     if (perr == (INT8U *)0) {  
  75.         OS_SAFETY_CRITICAL_EXCEPTION();  
  76.         return ((OS_EVENT *)0);  
  77.     }  
  78. #endif  
  79.   
  80. #if OS_ARG_CHK_EN > 0u  
  81.     if (pevent == (OS_EVENT *)0) {                         /* Validate 'pevent'                        */  
  82.         *perr = OS_ERR_PEVENT_NULL;  
  83.         return (pevent);  
  84.     }  
  85. #endif  
  86.     if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {      /* Validate event block type                */  
  87.         *perr = OS_ERR_EVENT_TYPE;  
  88.         return (pevent);  
  89.     }  
  90.     if (OSIntNesting > 0u) {                               /* See if called from ISR ...               */  
  91.         *perr = OS_ERR_DEL_ISR;                             /* ... can't DELETE from an ISR             */  
  92.         return (pevent);  
  93.     }  
  94.     OS_ENTER_CRITICAL();  
  95.     if (pevent->OSEventGrp != 0u) {                        /* See if any tasks waiting on mutex        */  
  96.         tasks_waiting = OS_TRUE;                           /* Yes                                      */  
  97.     } else {  
  98.         tasks_waiting = OS_FALSE;                          /* No                                       */  
  99.     }  
  100.     switch (opt) {  //opt为删除类型  
  101.         case OS_DEL_NO_PEND: //任务没有挂起的时候删除互斥信号        /* DELETE MUTEX ONLY IF NO TASK WAITING --- */  
  102.              if (tasks_waiting == OS_FALSE) {  
  103. #if OS_EVENT_NAME_EN > 0u  
  104.                  pevent->OSEventName   = (INT8U *)(void *)"?";  
  105. #endif  
  106.                  pcp                   = (INT8U)(pevent->OSEventCnt >> 8u);  
  107.                  if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {  
  108.                      OSTCBPrioTbl[pcp] = (OS_TCB *)0;    //释放占用的PCP提升资源          */  
  109.                  }  
  110.                  pevent->OSEventType   = OS_EVENT_TYPE_UNUSED;  
  111.                  pevent->OSEventPtr    = OSEventFreeList;  /* Return Event Control Block to free list  */  
  112.                  pevent->OSEventCnt    = 0u;  
  113.                  OSEventFreeList       = pevent;//重新将互斥量链接到空闲表  
  114.                  OS_EXIT_CRITICAL();  
  115.                  *perr                 = OS_ERR_NONE;  
  116.                  pevent_return         = (OS_EVENT *)0;    /* Mutex has been deleted                   */  
  117.              } else {  
  118.                  OS_EXIT_CRITICAL(); //存在将返回错误,此时不能删除  
  119.                  *perr                 = OS_ERR_TASK_WAITING;  
  120.                  pevent_return         = pevent;  
  121.              }  
  122.              break;  
  123.   
  124.         case OS_DEL_ALWAYS:       //总是删除互斥量                         /* ALWAYS DELETE THE MUTEX ---------------- */  
  125.              pcp  = (INT8U)(pevent->OSEventCnt >> 8u);                       /* Get PCP of mutex       */  
  126.              if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {  
  127.                  prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); /* Get owner's orig prio  */  
  128.                  ptcb = (OS_TCB *)pevent->OSEventPtr;  
  129.                  if (ptcb != (OS_TCB *)0) {                /* See if any task owns the mutex           */  
  130.                      if (ptcb->OSTCBPrio == pcp) {         /* See if original prio was changed         */  
  131.                          OSMutex_RdyAtPrio(ptcb, prio);    /* Yes, Restore the task's original prio    */  
  132.                      }  
  133.                  }  
  134.              }  
  135.              //别占用的资源强制释放  
  136.              while (pevent->OSEventGrp != 0u) {            /* Ready ALL tasks waiting for mutex        */  
  137.                  (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_ABORT);  
  138.              }  
  139. #if OS_EVENT_NAME_EN > 0u  
  140.              pevent->OSEventName   = (INT8U *)(void *)"?";  
  141. #endif  
  142.              pcp                   = (INT8U)(pevent->OSEventCnt >> 8u);  
  143.              if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {  
  144.                  OSTCBPrioTbl[pcp] = (OS_TCB *)0;          /* Free up the PCP                          */  
  145.              }  
  146.              pevent->OSEventType   = OS_EVENT_TYPE_UNUSED;  
  147.              pevent->OSEventPtr    = OSEventFreeList;      /* Return Event Control Block to free list  */  
  148.              pevent->OSEventCnt    = 0u;  
  149.              OSEventFreeList       = pevent;               /* Get next free event control block        */  
  150.              OS_EXIT_CRITICAL();  
  151.              if (tasks_waiting == OS_TRUE) {               /* Reschedule only if task(s) were waiting  */  
  152.                  OS_Sched();    //存在就绪任务将开展调度                           /* Find highest priority task ready to run  */  
  153.              }  
  154.              *perr         = OS_ERR_NONE;  
  155.              pevent_return = (OS_EVENT *)0;                /* Mutex has been deleted                   */  
  156.              break;  
  157.   
  158.         default:  
  159.              OS_EXIT_CRITICAL();  
  160.              *perr         = OS_ERR_INVALID_OPT; //无效opt输入  
  161.              pevent_return = pevent;  
  162.              break;  
  163.     }  
  164.     return (pevent_return); //返回删除的互斥量地址  
  165. }  

该任务主要是删除一个互斥量。
1、不占用任务删除类型:互斥量将在没有任务挂起时删除
2、总是删除类型:互斥量将总是删除任务,当存在任务挂起时将强制就绪释放

[cpp] view plain copy
  1. INT8U  OSMutexQuery (OS_EVENT       *pevent,  
  2.                      OS_MUTEX_DATA  *p_mutex_data)  
  3. {  
  4.     INT8U       i;  
  5.     OS_PRIO    *psrc;  
  6.     OS_PRIO    *pdest;  
  7. #if OS_CRITICAL_METHOD == 3u                     /* Allocate storage for CPU status register           */  
  8.     OS_CPU_SR   cpu_sr = 0u;  
  9. #endif  
  10.   
  11.     if (OSIntNesting > 0u) {                               /* See if called from ISR ...               */  
  12.         return (OS_ERR_QUERY_ISR);                         /* ... can't QUERY mutex from an ISR        */  
  13.     }  
  14. #if OS_ARG_CHK_EN > 0u  
  15.     if (pevent == (OS_EVENT *)0) {                         /* Validate 'pevent'                        */  
  16.         return (OS_ERR_PEVENT_NULL);  
  17.     }  
  18.     if (p_mutex_data == (OS_MUTEX_DATA *)0) {              /* Validate 'p_mutex_data'                  */  
  19.         return (OS_ERR_PDATA_NULL);  
  20.     }  
  21. #endif  
  22.     if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {      /* Validate event block type                */  
  23.         return (OS_ERR_EVENT_TYPE);  
  24.     }  
  25.     OS_ENTER_CRITICAL();  
  26.     p_mutex_data->OSMutexPCP  = (INT8U)(pevent->OSEventCnt >> 8u);  
  27.     p_mutex_data->OSOwnerPrio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);  
  28.     if (p_mutex_data->OSOwnerPrio == 0xFFu) {  
  29.         p_mutex_data->OSValue = OS_TRUE; //查看资源是否被占用  
  30.     } else {  
  31.         p_mutex_data->OSValue = OS_FALSE;  
  32.     }  
  33.     p_mutex_data->OSEventGrp  = pevent->OSEventGrp;        /* Copy wait list                           */  
  34.     psrc                      = &pevent->OSEventTbl[0];  
  35.     pdest                     = &p_mutex_data->OSEventTbl[0];  
  36.     for (i = 0u; i < OS_EVENT_TBL_SIZE; i++) { //执行数据拷贝  
  37.         *pdest++ = *psrc++;  
  38.     }  
  39.     OS_EXIT_CRITICAL();  
  40.     return (OS_ERR_NONE);  
  41. }  
  42. //该函数比较简单,直接拷贝pevent信息到p_mutex_data数据结构中。  
  43. static  void  OSMutex_RdyAtPrio (OS_TCB  *ptcb,  
  44.                                 INT8U    prio)  
  45. {  
  46.     INT8U  y;  
  47.   
  48.   
  49.     y            =  ptcb->OSTCBY;                          /* Remove owner from ready list at 'pcp'    */  
  50.     OSRdyTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX; //清除原就绪标志  
  51.     if (OSRdyTbl[y] == 0u) {  
  52.         OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;  
  53.     }  
  54.     ptcb->OSTCBPrio         = prio;  
  55.     OSPrioCur               = prio;                        /* The current task is now at this priority */  
  56. #if OS_LOWEST_PRIO <= 63u  
  57.     ptcb->OSTCBY            = (INT8U)((INT8U)(prio >> 3u) & 0x07u);  
  58.     ptcb->OSTCBX            = (INT8U)(prio & 0x07u);  
  59. #else  
  60.     ptcb->OSTCBY            = (INT8U)((INT8U)(prio >> 4u) & 0x0Fu);  
  61.     ptcb->OSTCBX            = (INT8U) (prio & 0x0Fu);  
  62. #endif  
  63.     ptcb->OSTCBBitY         = (OS_PRIO)(1uL << ptcb->OSTCBY); //在新的优先级上就绪任务  
  64.     ptcb->OSTCBBitX         = (OS_PRIO)(1uL << ptcb->OSTCBX);  
  65.     OSRdyGrp               |= ptcb->OSTCBBitY;             /* Make task ready at original priority     */  
  66.     OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;  
  67.     OSTCBPrioTbl[prio]      = ptcb;   
  68. }  

该函数主要是将一个任务优先级提升到prio层次,并将提升后的任务就绪。

到这里,互斥信号量的源码分析也就结束了,总结一点,互斥信号量和信号量其实大同小异,当然这是考虑信号量在只有一个时(及cnt=1)的情况,二者仅有的区别在于互斥信号量可以有效地防止优先级反转,而这在一些安全相关的系统中将是至关重要的。因此在处理对任务顺序较为敏感的任务的时候,需要使用互斥量来防止反转,增加系统的可预测性。
好了,互斥量就讲到这里
时间:170708  
原创粉丝点击