从零开始学习UCOSII操作系统8--互斥型信号量

从零开始学习UCOSII操作系统8--互斥型信号量

1、互斥型信号量的定义

(1)任务可以用互斥型信号量实现对共享资源的独占式处理,互斥型信号量也称为mutex,mutex是二值信号量,不但具有UCOSII普通信号量的机制外,还具有其他的一些特性。

(2)最重要的一点是,可以解除优先级反转的问题。当高优先级的任务需要使用某个共享资源的时候,而该资源已被一个低优先级反转的问题,就会发生优先级反转的问题。对于这个问题:内核将低优先级提升到高于那个高优先级的任务,直到低优先级的任务使用完占用的共享资源。

2、互斥型信号量的使用原理

(1)建立一个互斥型信号量OSMutexCreate()
在使用mutex之前,必须建立它,建立mutex是通过调用函数OSMutexCreate()来完成的。

OS_EVENT  *OSMutexCreate (INT8U   prio,
                          INT8U  *perr)
{
    OS_EVENT  *pevent;

#if OS_ARG_CHK_EN > 0u
    if (prio >= OS_LOWEST_PRIO) {                          /* 如果优先级大于最低的优先级                           */
        *perr = OS_ERR_PRIO_INVALID;
        return ((OS_EVENT *)0);
    }
#endif
    if (OSIntNesting > 0u) {                               /* See if called from ISR ...               */
        *perr = OS_ERR_CREATE_ISR;                         /*不能在一个中断中设置互斥型信号量*/
        return ((OS_EVENT *)0);
    }
    OS_ENTER_CRITICAL();
    if (OSTCBPrioTbl[prio] != (OS_TCB *)0) {               /* 互斥型信号量优先级存在  */
        OS_EXIT_CRITICAL();                                /* Task already exist at priority ...       */
        *perr = OS_ERR_PRIO_EXIST;                         /* ... inheritance priority                 */
        return ((OS_EVENT *)0);
    }
    OSTCBPrioTbl[prio] = OS_TCB_RESERVED;                  /* Reserve the table entry                  */
    pevent             = OSEventFreeList;                  /* 得到一个空闲的链表   */
    if (pevent == (OS_EVENT *)0) {                         /* See if an ECB was available              */
        OSTCBPrioTbl[prio] = (OS_TCB *)0;                  /* No, Release the table entry              */
        OS_EXIT_CRITICAL();
        *perr              = OS_ERR_PEVENT_NULL;           /* No more event control blocks             */
        return (pevent);
    }
    OSEventFreeList        = (OS_EVENT *)OSEventFreeList->OSEventPtr;   /* Adjust the free list        */
    OS_EXIT_CRITICAL();
    pevent->OSEventType    = OS_EVENT_TYPE_MUTEX;
    pevent->OSEventCnt     = (INT16U)((INT16U)prio << 8u) | OS_MUTEX_AVAILABLE; /* Resource is avail.  */
    pevent->OSEventPtr     = (void *)0;                                 /*初始化为没有任何的任务拥有这个任务   */

    OS_EventWaitListInit(pevent);
    *perr                  = OS_ERR_NONE;
    return (pevent);
}

(2)等待一个互斥型信号量(挂起),OSMutexPend()

使用前面的计数信号量。
1、不支持嵌套操作。

2、不支持所有者,任意的任务都能发送释放。

3、无法解决优先级反转的问题。

对于请求信号量的操作有以下的几种操作:

(1)当信号量已经被自己占有的时候,再次请求,就+1
(2)当有一个高优先级的任务已经占有这个信号量的时候,
现在有一个低优先级的任务进来了,显然这个任务是需要进行等待的。
(3)当有一个低优先级的任务已经占有这个信号量的时候,
现在有一个高优先级的任务进来了,显然这个时候,低优先级的任务需要暂停的提高到高优先级的任务中来。

互斥型信号量的请求:
参数定义:
(1)参数1:OS_EVENT pevent :定义一个指针,类型是指向这个事件控制块的指针。
(2)参数2:timeout:超时等待
(3)参数3:
perr :返回的输出型参数:代表出错的类型

void  OSMutexPend (OS_EVENT  *pevent,
                   INT32U     timeout,
                   INT8U     *perr)
{
    INT8U      pip;                                        /* Priority Inheritance Priority (PIP)      */
    INT8U      mprio;                                      /* Mutex owner priority                     */
    BOOLEAN    rdy;                                        /* Flag indicating task was ready           */
    OS_TCB    *ptcb;
    OS_EVENT  *pevent2;
    INT8U      y;

/*各种创建的时候的异常的代码*/

    OS_ENTER_CRITICAL();
    pip = (INT8U)(pevent->OSEventCnt >> 8u);              
         /* 该信号量继承的优先级*/

     /* 当互斥信号量还没有被占用的时候 */
    if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) 
    {
        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;       
        /* 直接以当前的优先级占用它 */

        pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;   

         /*  保存拥有者的优先级    */
        pevent->OSEventPtr  = (void *)OSTCBCur;            

        /*    事件控制块的指针,指向任务控制块,把任务挂起到事件链表中执行   */

        if (OSTCBCur->OSTCBPrio <= pip) 
        {                  
        /*  PIP的优先级必须更小,不然的话,就会发生优先级反转 */
            OS_EXIT_CRITICAL();                            /*      ... than current task!              */
            *perr = OS_ERR_PIP_LOWER;
        } 
        else 
        {
            OS_EXIT_CRITICAL();
            *perr = OS_ERR_NONE;
        }
        return;
    }
    mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); 
     /* 占用该信号量的拥有者的任务优先级   */

    //事件控制块的指针
    ptcb  = (OS_TCB *)(pevent->OSEventPtr);        

     /*  占用该信号量的任务的TCB  */
    if (ptcb->OSTCBPrio > pip)
     {                                 
         /*  如果当前所有者优先级比继承的优先级大*/
        if (mprio > OSTCBCur->OSTCBPrio)
        {
            y = ptcb->OSTCBY;

            /*并且互斥型信号量拥有者处于就绪状态的话,那么就需要将其从就绪态中移除*/

            if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != 0u) {          /*     See if mutex owner is ready   */
                OSRdyTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX;   

                /* 把优先级进行反转之后移除现在占有该信号量的任务 */
                if (OSRdyTbl[y] == 0u) 
                {                          
                    /* ... list at current prio */
                    OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;
                }
                rdy = OS_TRUE;
            } 
            else  /*占有该信号量的任务现在在事件的等待列表中*/
            {
                pevent2 = ptcb->OSTCBEventPtr;
                if (pevent2 != (OS_EVENT *)0)
                 {                  
                     /* 从事件列表中移除此任务       */
                    y = ptcb->OSTCBY;
                    pevent2->OSEventTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX;
                    if (pevent2->OSEventTbl[y] == 0u) {
                        pevent2->OSEventGrp &= (OS_PRIO)~ptcb->OSTCBBitY;
                    }
                }
                rdy = OS_FALSE;                          
            }

            ptcb->OSTCBPrio = pip;   

 /* Change owner task prio to PIP            */

#if OS_LOWEST_PRIO <= 63u
            ptcb->OSTCBY    = (INT8U)( ptcb->OSTCBPrio >> 3u);
            ptcb->OSTCBX    = (INT8U)( ptcb->OSTCBPrio & 0x07u);
#else
            ptcb->OSTCBY    = (INT8U)((INT8U)(ptcb->OSTCBPrio >> 4u) & 0xFFu);
            ptcb->OSTCBX    = (INT8U)( ptcb->OSTCBPrio & 0x0Fu);
#endif
            ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY);
            ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX);

            if (rdy == OS_TRUE) 
            {                          
            /*如果之前占有该信号量的任务已经接近于就绪态了
                就将提升之后的优先级加入到就绪表中*/
                OSRdyGrp               |= ptcb->OSTCBBitY; /* ... make it ready at new priority.       */
                OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
            } 
            else 
            {
                pevent2 = ptcb->OSTCBEventPtr;
                if (pevent2 != (OS_EVENT *)0) {           
             /* Add to event wait list                   */
                    pevent2->OSEventGrp               |= ptcb->OSTCBBitY;
                    pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                }
            }
            OSTCBPrioTbl[pip] = ptcb;
        }
    }
    OSTCBCur->OSTCBStat     |= OS_STAT_MUTEX;        
     /* Mutex not available, pend current task        */
    OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;
    OSTCBCur->OSTCBDly       = timeout;               
     /* Store timeout in current task's TCB           */
    OS_EventTaskWait(pevent);                         
    /* Suspend task until event or timeout occurs    */
    OS_EXIT_CRITICAL();
    OS_Sched();                                       
    /*  因为已经切换任务了,查找最高优先级的任务    */
    OS_ENTER_CRITICAL();

    //如果事件控制块的指针是指向挂起状态的
    switch (OSTCBCur->OSTCBStatPend) 
    {                
        /* See if we timed-out or aborted                */
        case OS_STAT_PEND_OK:
             *perr = OS_ERR_NONE;
             break;

        case OS_STAT_PEND_ABORT:
             *perr = OS_ERR_PEND_ABORT;               /* Indicate that we aborted getting mutex        */
             break;

        case OS_STAT_PEND_TO:
        default:
             OS_EventTaskRemove(OSTCBCur, pevent);
             *perr = OS_ERR_TIMEOUT;                  /* Indicate that we didn't get mutex within TO   */
             break;
    }
    OSTCBCur->OSTCBStat          =  OS_STAT_RDY;     
         /* Set   task  status to ready                   */
    OSTCBCur->OSTCBStatPend      =  OS_STAT_PEND_OK; 
         /* Clear pend  status                            */
    OSTCBCur->OSTCBEventPtr      = (OS_EVENT  *)0;   
         /* Clear event pointers                          */
#if (OS_EVENT_MULTI_EN > 0u)
    OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
    OS_EXIT_CRITICAL();
}

互斥型信号量释放的操作函数:

只有在同一个任务中请求互斥型函数,才能同一个任务中释放互斥型函数。

INT8U  OSMutexPost (OS_EVENT *pevent)
{
    INT8U      pip;                                   
    /* Priority inheritance priority                 */
    INT8U      prio;

    /* 异常处理 */

    OS_ENTER_CRITICAL();
    pip  = (INT8U)(pevent->OSEventCnt >> 8u);        
     /*得到互斥型信号量的优先级 */
    prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); 
     /* 得到拥有者原来的优先级   */
    if (OSTCBCur != (OS_TCB *)pevent->OSEventPtr)
     {   
        /* 进入到这个函数中的话,说明请求互斥型信号量的操作,被其他的任务释放,这是不符合互斥型信号量的,返回一个错误值 */
        OS_EXIT_CRITICAL();
        return (OS_ERR_NOT_MUTEX_OWNER);
    }
    if (OSTCBCur->OSTCBPrio == pip) 
    {                 
        /* 查看占用mutex的任务的优先级是否已经升到了PIP,因为有一个更高的优先级的任务也需要整个mutex,在这种情况下,占用mutex的任务被将回到原来的优先级 */
        OSMutex_RdyAtPrio(OSTCBCur, prio);            

    }
    OSTCBPrioTbl[pip] = OS_TCB_RESERVED;             
         /* Reserve table entry                           */
    /*如果互斥性信号量存在的话*/
    if (pevent->OSEventGrp != 0u) 
    {                   
        /* 有没有其他的任务在等待这个信号量 */

         /* Yes, Make HPT waiting for mutex ready         */
        prio                = OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);
        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;  
        /* 保存新的拥有者的优先级  */

        pevent->OSEventCnt |= prio;
        pevent->OSEventPtr  = OSTCBPrioTbl[prio];  

        /* Link to new mutex owner's OS_TCB         */
        if (prio <= pip) 
        {                           
         /*      PIP 'must' have a SMALLER prio ...       */
            OS_EXIT_CRITICAL();                       
        /*      ... than current task!                   */
            OS_Sched();                               
        /*      Find highest priority task ready to run  */
            return (OS_ERR_PIP_LOWER);
        } 
        else
         {
            OS_EXIT_CRITICAL();
            OS_Sched();                              
     /*      Find highest priority task ready to run  */
            return (OS_ERR_NONE);
        }
    }
    pevent->OSEventCnt |= OS_MUTEX_AVAILABLE;         /* No,  Mutex is now available                   */
    pevent->OSEventPtr  = (void *)0;
    OS_EXIT_CRITICAL();
    return (OS_ERR_NONE);
}

(3)无等待的获取互斥型信号量(任务不挂起)OSMutexAccept();

相当于查询的功能:
参数1:事件控制块的指针:
参数2:范回的错误值:

BOOLEAN  OSMutexAccept (OS_EVENT  *pevent,
                        INT8U     *perr)
{
    INT8U      pip;                                   

    /*  异常处理  */

    OS_ENTER_CRITICAL();                             
    /* Get value (0 or 1) of Mutex                  */

    pip = (INT8U)(pevent->OSEventCnt >> 8u);         
      /*   得到互斥型信号量的优先级     */

    if ((pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) 
    {
        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;   

/*      Mask off LSByte (Acquire Mutex)         */
        pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;     

/* 如果互斥型信号量有效,那么OSMutex把调用该函数任务的优先级写到
OSEventCnt的低8位,并将mutex的事件控制块ECB链接到该任务的任务控制块。  */
        pevent->OSEventPtr  = (void *)OSTCBCur;        

/*      Link TCB of task owning Mutex           */
        if (OSTCBCur->OSTCBPrio <= pip) 
        {              

/*      PIP 'must' have a SMALLER prio ...      */
            OS_EXIT_CRITICAL();                       

     /*      ... than current task!                  */
            *perr = OS_ERR_PIP_LOWER;
        } 
        else 
        {
            OS_EXIT_CRITICAL();
            *perr = OS_ERR_NONE;
        }
        return (OS_TRUE);
    }
    OS_EXIT_CRITICAL();
    *perr = OS_ERR_NONE;
    return (OS_FALSE);
}
  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值