uCOS-II互斥型信号量的相关操作函数

定位到uCOS-II/Source/os_mutex.c,该文件是互斥型信号量的相关操作函数。互斥型信号量也就是互斥锁Mutex,是一个二值(0/1)信号量。在操作共享资源时,使用Mutex可以保证满足互斥条件。

目录

1. 非阻塞的获取互斥型信号量函数OSMutexAccept()

2. 创建和初始化互斥型信号量函数OSMutexCreate()

3. 删除互斥型信号量函数OSMutexDel()

4. 阻塞获取一个互斥型信号量函数OSMutexPend()

5. 释放一个互斥型信号量函数OSMutexPost()

6. 获取Mutex的当前状态信息函数OSMutexQuery()

7. 还原任务的优先级函数OSMutex_RdyAtPrio()


1. 非阻塞的获取互斥型信号量函数OSMutexAccept()

  OSMutexAccept()用于检测Mutex以判断是否可用,若资源不可用,调用此函数不会使得所在任务被挂起。

//允许使用os中的Mutex
#if OS_MUTEX_ACCEPT_EN > 0u
BOOLEAN  OSMutexAccept (OS_EVENT  *pevent,  //pevent指向管理着某资源的Mutex,
                        INT8U     *perr)    //perr为输出型参数用于表示出错类型
{
    INT8U      pcp;

    //临界区保护方法3
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION(); //与系统异常相关,找不到定义,不细究
        return (OS_FALSE);
    }
#endif

//检测参数宏使能
#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        *perr = OS_ERR_PEVENT_NULL;
        return (OS_FALSE);
    }
#endif
    //本函数是支持MUTEX的,若非OS_EVENT_TYPE_MUTEX,返回错误
    if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {
        *perr = OS_ERR_EVENT_TYPE;
        return (OS_FALSE);
    }
    //不能再ISR中试图获取二值信号量
    if (OSIntNesting > 0u) {
        *perr = OS_ERR_PEND_ISR;
        return (OS_FALSE);
    }

    //进入临界区
    OS_ENTER_CRITICAL();
    //高8位为天花板优先级,其优先等级大于所有可以获取该Mutex的任务的优先级
    pcp = (INT8U)(pevent->OSEventCnt >> 8u);    

    //低8位全为1表示该Mutex没有被任务获取过,Mutex可用
    if ((pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) {

        //将天花板优先级设置到pevent->OSEventCnt,其实这句话不起效果,
        //pevent->OSEventCnt本来就为天花板优先级
        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;

        //将任务优先级设置到低8位,以表示该互斥锁被任务获取了
        pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;

        //将事件控制块连接到该任务控制块
        pevent->OSEventPtr  = (void *)OSTCBCur; 

        if ((pcp != OS_PRIO_MUTEX_CEIL_DIS) &&  //OS_PRIO_MUTEX_CEIL_DIS表示禁用将优先级提升天花板 
            (OSTCBCur->OSTCBPrio <= pcp)) {     //天花板的优先级必须不小于获取该Mutex的任务,否则说明在创建Mutex的时候
                                                //天花板优先级设置出错
             OS_EXIT_CRITICAL();
            *perr = OS_ERR_PCP_LOWER;
        } else {
             OS_EXIT_CRITICAL();
            *perr = OS_ERR_NONE;                //正常执行
        }
        return (OS_TRUE);
    }
    OS_EXIT_CRITICAL();
    *perr = OS_ERR_NONE;
    return (OS_FALSE);
}
#endif


2. 创建和初始化互斥型信号量函数OSMutexCreate()

  互斥型信号量mutual的建立和初始化. 在操作共享资源时, 使用Mutex可以保证满足互斥条件。

OS_EVENT  *OSMutexCreate (INT8U   prio,     //用于设置Mutex的天花板优先级
                          INT8U  *perr)     //用于输出错误信息      
{
    OS_EVENT  *pevent;
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((OS_EVENT *)0);
    }
#endif

#ifdef OS_SAFETY_CRITICAL_IEC61508
    if (OSSafetyCriticalStartFlag == OS_TRUE) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((OS_EVENT *)0);
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (prio != OS_PRIO_MUTEX_CEIL_DIS) {   //OS_PRIO_MUTEX_CEIL_DIS表禁用将优先级提升天花板
        if (prio >= OS_LOWEST_PRIO) {       //prio大于OS_LOWEST_PRIO,非法
           *perr = OS_ERR_PRIO_INVALID;
            return ((OS_EVENT *)0);
        }
    }
#endif
    if (OSIntNesting > 0u) {
        *perr = OS_ERR_CREATE_ISR; 
        return ((OS_EVENT *)0);
    }
    OS_ENTER_CRITICAL();
    if (prio != OS_PRIO_MUTEX_CEIL_DIS) {
        //判断天花板优先级对应的任务是否存在,不等于0表示存在
        if (OSTCBPrioTbl[prio] != (OS_TCB *)0) { 
            OS_EXIT_CRITICAL();
           *perr = OS_ERR_PRIO_EXIST;
            return ((OS_EVENT *)0);
        }

        //不存在则天花板优先级占据的TCB空间占据
        OSTCBPrioTbl[prio] = OS_TCB_RESERVED; 
    }

    pevent = OSEventFreeList;   //得到一个空闲的事件控制块

    //等于0表示事件控制块链表断开了
    if (pevent == (OS_EVENT *)0) {
        if (prio != OS_PRIO_MUTEX_CEIL_DIS) {
            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;
    OS_EXIT_CRITICAL();

    //成功申请到一个事件链表中的节点,现在进行初始化
    pevent->OSEventType = OS_EVENT_TYPE_MUTEX;  //设置事件类型

    //高8位为天花板优先级;因为该事件现在还不被某个任务申请,所以低8位为OS_MUTEX_AVAILABLE表示该事件可用
    //若被任务申请了,则低8位为该任务的优先级
    pevent->OSEventCnt  = (INT16U)((INT16U)prio << 8u) | OS_MUTEX_AVAILABLE; 

    //OSEventPtr指向0,表示该任务没有MUTEX
    pevent->OSEventPtr  = (void *)0;
#if OS_EVENT_NAME_EN > 0u
    pevent->OSEventName = (INT8U *)(void *)"?";
#endif
    OS_EventWaitListInit(pevent);  //初始化pevent中的WaitList,即没有任务在等待该Mutex
   *perr = OS_ERR_NONE;
    return (pevent);
}

OS_EventWaitListInit()函数实现体为:
/* 初始化pevent中的 WaitList*/
#if (OS_EVENT_EN)
void  OS_EventWaitListInit (OS_EVENT *pevent)
{
#if OS_LOWEST_PRIO <= 63
    INT8U  *ptbl;
#else
    INT16U *ptbl;
#endif
    INT8U   i;

    /* 置零操作OSEventGrp和OSEventTbl */
    pevent->OSEventGrp = 0;                      /* No task waiting on event                           */
    ptbl               = &pevent->OSEventTbl[0];

    for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
        *ptbl++ = 0;
    }
}



3. 删除互斥型信号量函数OSMutexDel()

  删除Mutex操作是有风险的,因为可能存在任务它还想使用这个实际上已被删除的Mutex,所以要删除一个Mutex首先应删除等待这个Mutex的所有任务。

#if OS_MUTEX_DEL_EN > 0u        //Mutex删除使能宏
OS_EVENT  *OSMutexDel (OS_EVENT  *pevent,   //指向Mutex的指针
                       INT8U      opt,      //删除Mutex的条件,OS_DEL_NO_PEND/OS_DEL_ALWAYS
                       INT8U     *perr)     //指向出错信息
{
    BOOLEAN    tasks_waiting;
    OS_EVENT  *pevent_return;
    INT8U      pcp;
    INT8U      prio;
    OS_TCB    *ptcb;
#if OS_CRITICAL_METHOD == 3u
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((OS_EVENT *)0);
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        *perr = OS_ERR_PEVENT_NULL;
        return (pevent);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {
        *perr = OS_ERR_EVENT_TYPE;
        return (pevent);
    }
    if (OSIntNesting > 0u) {
        *perr = OS_ERR_DEL_ISR;
        return (pevent);
    }
    OS_ENTER_CRITICAL();
    //OSEventGrp不等于0表示有任务正在等待预备删除的Mutex
    if (pevent->OSEventGrp != 0u) { 
        tasks_waiting = OS_TRUE; 
    } else {
        tasks_waiting = OS_FALSE;
    }

    //根据删除条件执行删除操作
    switch (opt) {
        //OS_DEL_NO_PEND表没有任务在等待才删除,反之不删除
        case OS_DEL_NO_PEND:  
             //没任务在等待,删除之,回收该空间
             if (tasks_waiting == OS_FALSE) {
#if OS_EVENT_NAME_EN > 0u
                 pevent->OSEventName   = (INT8U *)(void *)"?";
#endif
                 //取出天花板优先级所在的任务TCB内存空间
                 pcp                   = (INT8U)(pevent->OSEventCnt >> 8u);
                 if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {
                     OSTCBPrioTbl[pcp] = (OS_TCB *)0; 
                 }
                 pevent->OSEventType   = OS_EVENT_TYPE_UNUSED;
                 pevent->OSEventPtr    = OSEventFreeList;  /* Return Event Control Block to free list  */
                 pevent->OSEventCnt    = 0u;
                 OSEventFreeList       = pevent;
                 OS_EXIT_CRITICAL();
                 *perr                 = OS_ERR_NONE;
                 pevent_return         = (OS_EVENT *)0; 
             } else {  //有任务在等待,不能删除且报错
                 OS_EXIT_CRITICAL();
                 *perr                 = OS_ERR_TASK_WAITING;
                 pevent_return         = pevent;
             }
             break;

        case OS_DEL_ALWAYS:   //不管有无任务在等待该Mutex都删除
            //取出天花板优先级所在的任务TCB内存空间
             pcp  = (INT8U)(pevent->OSEventCnt >> 8u); 
             if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {

                 //取出占据该Mutex的任务的原优先级
                 prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);
                 //取出占据该Mutex的任务的TCB
                 ptcb = (OS_TCB *)pevent->OSEventPtr;

                 //确实存在占据该Mutex的任务
                 if (ptcb != (OS_TCB *)0) {
                     //若该任务的优先级已经被提升至天花板,则恢复其原优先级
                     if (ptcb->OSTCBPrio == pcp) {  
                         OSMutex_RdyAtPrio(ptcb, prio); 
                     }
                 }
             }
             //环形所有在等待该Mutex的任务,不然一旦该Mutex被删除,这些任务将陷入一直等待中
             while (pevent->OSEventGrp != 0u) {
                 (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_ABORT);
             }

             //接下来的删除操作跟前面一样了
#if OS_EVENT_NAME_EN > 0u
             pevent->OSEventName   = (INT8U *)(void *)"?";
#endif
             pcp                   = (INT8U)(pevent->OSEventCnt >> 8u);
             if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {
                 OSTCBPrioTbl[pcp] = (OS_TCB *)0;
             }
             pevent->OSEventType   = OS_EVENT_TYPE_UNUSED;
             pevent->OSEventPtr    = OSEventFreeList;
             pevent->OSEventCnt    = 0u;
             OSEventFreeList       = pevent; 
             OS_EXIT_CRITICAL();
             if (tasks_waiting == OS_TRUE) {  
                 OS_Sched(); 
             }
             *perr         = OS_ERR_NONE;
             pevent_return = (OS_EVENT *)0;
             break;

        default:
             OS_EXIT_CRITICAL();
             *perr         = OS_ERR_INVALID_OPT;    //删除条件出错
             pevent_return = pevent;
             break;
    }
    return (pevent_return);
}
#endif


4. 阻塞获取一个互斥型信号量函数OSMutexPend()

  当任务需要独占共享资源时候,可以使用OSMutexPend()函数获得一个Mutex对该资源加以保护。该函数若获得到了Mutex将返回,反之调用此函数的任务进入挂起等待状态,直到获取到目标Mutex或者超时。需要注意的是,如果调用此函数获取Mutex,而该Mutex已经被低优先级任务占用了,那么此函数会将占据该Mutex的低优先级任务的优先级提升至天花板。

void  OSMutexPend (OS_EVENT  *pevent,   //要获取的目的Mutex
                   INT32U     timeout   //超时时间
                   INT8U     *perr)
{
    INT8U      pcp;
    INT8U      mprio;
    BOOLEAN    rdy;
    OS_TCB    *ptcb;
    OS_EVENT  *pevent2;
    INT8U      y;
#if OS_CRITICAL_METHOD == 3u 
    OS_CPU_SR  cpu_sr = 0u;
#endif

#ifdef OS_SAFETY_CRITICAL
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) { 
        *perr = OS_ERR_PEVENT_NULL;
        return;
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {
        *perr = OS_ERR_EVENT_TYPE;
        return;
    }
    if (OSIntNesting > 0u) { 
        *perr = OS_ERR_PEND_ISR;
        return;
    }
    if (OSLockNesting > 0u) { 
        *perr = OS_ERR_PEND_LOCKED; 
        return;
    }

    OS_ENTER_CRITICAL();
    //拿到天花板优先级
    pcp = (INT8U)(pevent->OSEventCnt >> 8u); 

    //等于OS_MUTEX_AVAILABLE表示该Mutex之前没有被任务任务使用过
    if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) {
        //设置Mutex的天花板优先级
        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;

        //将任务优先级设置到低8位,以表示该互斥锁被任务获取了
        pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;

        //将事件控制块连接到该任务控制块
        pevent->OSEventPtr  = (void *)OSTCBCur; 
        if ((pcp != OS_PRIO_MUTEX_CEIL_DIS) &&  //OS_PRIO_MUTEX_CEIL_DIS表示禁用将优先级提升天花板 
            (OSTCBCur->OSTCBPrio <= pcp)) {     //天花板的优先级必须不小于获取该Mutex的任务,否则说明在创建Mutex的时候
             OS_EXIT_CRITICAL();                //天花板优先级设置出错
            *perr = OS_ERR_PCP_LOWER;
        } else {
             OS_EXIT_CRITICAL();
            *perr = OS_ERR_NONE;
        }
        return;
    }

    if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {
        //低8位存储了拥有该Mutex的任务的优先级
        mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); 

        /* 一开始OSEventPtr是用来在event链表中指向下一个节点的,当Mutex有任务获取了它之后,它就没用处了,
        用来指向了获取了该Mutex的任务的TCB。所以说OSEventPtr取值有3种可能:
        (1) Mutex还没被获取时,在event链表中指向一个格子,该格子用于存放Mutex
        (2) Mutex被获取了,但是还没跟任务挂接上,取值为NULL
        (3) Mutex被获取了,且已经跟某个任务挂接上了,取值为该任务的TCB的地址 */

        //拿到当前占据该Mutex的任务的TCB
        ptcb  = (OS_TCB *)(pevent->OSEventPtr);
        if (ptcb->OSTCBPrio > pcp) { //天花板的优先级最高即数值最低,所以这个if一定会成立

            //如果当前任务的优先级大于拥有该Mutex的任务的优先级
            //这时候有可能发生任务优先级翻转,需要将拥有该Mutex的任务的优先级提升至天花板
            if (mprio > OSTCBCur->OSTCBPrio) 
            {
                y = ptcb->OSTCBY; /* 注意,ptcb是拥有Mutex的任务的tcb指针 */

                //不等于0表示就绪态
                if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != 0u) { 

                    //清除就绪态,即将该任务设置为非就绪态
                    OSRdyTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX; 
                    if (OSRdyTbl[y] == 0u) {
                        OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;
                    }
                    rdy = OS_TRUE;
                } 
                else   //拥有给Mutex的任务为非就绪态
                { 
                    //拿出该任务的等待事件列表
                    pevent2 = ptcb->OSTCBEventPtr;

                    //解引用前先判断是否为空指针
                    if (pevent2 != (OS_EVENT *)0) {

                        //将该任务的所有等待事件清空,其目的是让持有Mutex的任务赶快执行,执行
                        //完毕后才能轮到比它优先级高的当前任务执行
                        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 = pcp; //提升持有Mutex的任务的优先级提升至天花板
/* 以下四个变量跟任务的优先级密切相关,所以要随之更新 */
#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);

                //rdy是在上面设置的,为OS_TRUE表示一开始是就绪态非后改为非就绪态,现在又要将其改为就绪态。为什么?
                //因为中间改变了任务的优先级
                if (rdy == OS_TRUE) {
                    OSRdyGrp               |= ptcb->OSTCBBitY; 
                    OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                } else { //原来在等待某事件,清除等待事件,即还是设置为就绪态
                    pevent2 = ptcb->OSTCBEventPtr;
                    if (pevent2 != (OS_EVENT *)0) { 
                        pevent2->OSEventGrp               |= ptcb->OSTCBBitY;
                        pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                    }
                }
                /* tcb没被改变,但是在OSTCBPrioTbl的位置改变了,所以这里要做修改 */
                OSTCBPrioTbl[pcp] = ptcb;

                //到这里可以察觉,天花板pip所代表的优先级所在的格子必要要空着,即不能装载任务,
                //不然这里等于ptcb就会把原来装载的pip的普通任务冲掉了 */
            }
        }

    }
    /* 标志当前任务的状态,如OS_STAT_RDY就绪态,在这里标记当前任务正在等待Mutex */
    OSTCBCur->OSTCBStat     |= OS_STAT_MUTEX;

    /* 标志当前任务被挂起态 */
    OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;

    /* 挂起超时时间,注意,超时后就变为OS_STAT_PEND_TO状态而非就绪态 */ 
    OSTCBCur->OSTCBDly       = timeout; 

    /* os_corec定义的,实现将当前任务因等待事件而挂起:
    (1)把当前任务在pevent中标记出来,表示当前任务正在等待event。
    (2)把当前任务从就绪表中拿出来 
    */ 
    OS_EventTaskWait(pevent);
    OS_EXIT_CRITICAL();

    //调度
    OS_Sched();
    OS_ENTER_CRITICAL();

     /* 能执行到这里说明可能:
     (1) 原先持有mutex的任务已经放开Mutex了
     (2) 等待的Mutex被删除了
     (3) 超时
     于是就有了下面的分支
     */
    switch (OSTCBCur->OSTCBStatPend) { 
        case OS_STAT_PEND_OK:
             *perr = OS_ERR_NONE;
             break;

        case OS_STAT_PEND_ABORT:
             *perr = OS_ERR_PEND_ABORT;  
             break;

        case OS_STAT_PEND_TO:
        default:
             // 超时了还等待不到,直接从pevent等待event队列中删除本任务,不再等待
             OS_EventTaskRemove(OSTCBCur, pevent);
             *perr = OS_ERR_TIMEOUT;  
             break;
    }

    //设置任务的相关状态
    OSTCBCur->OSTCBStat          =  OS_STAT_RDY; 
    OSTCBCur->OSTCBStatPend      =  OS_STAT_PEND_OK; 
    OSTCBCur->OSTCBEventPtr      = (OS_EVENT  *)0;
#if (OS_EVENT_MULTI_EN > 0u)
    OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
    OS_EXIT_CRITICAL();
}


5. 释放一个互斥型信号量函数OSMutexPost()

  OSMutexPost()用于释放持有的Mutex。当任务已调用OSMutexAccept()或OSMutexPend()请求得到Mutex时,此函数才起作用。若持有Mutex的本任务的优先级已经被提升至天花板,那么此函数要恢复为原先的优先级;若有多个任务在等待被释放的Mutex,那么等待任务中优先级最高的任务获得Mutex;若没有等待该Mutex的任务那么只是设置相关的值。

INT8U  OSMutexPost (OS_EVENT *pevent)
{
    INT8U      pcp;  
    INT8U      prio;
#if OS_CRITICAL_METHOD == 3u  
    OS_CPU_SR  cpu_sr = 0u;
#endif

    if (OSIntNesting > 0u) { 
        return (OS_ERR_POST_ISR); 
    }
#if OS_ARG_CHK_EN > 0u
    if (pevent == (OS_EVENT *)0) {
        return (OS_ERR_PEVENT_NULL);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) { 
        return (OS_ERR_EVENT_TYPE);
    }
    OS_ENTER_CRITICAL();

    //拿到天花板优先级
    pcp  = (INT8U)(pevent->OSEventCnt >> 8u); 

    //占据Mutex的原来优先级,也就是当前任务
    prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8); 
    /*
        为什么在这里要用OSEventCnt来拿原先任务的优先级而不是像之前通过任务的TCB来拿?
        这是因为在获取Mutex的时候,本任务的优先级可能被提高了,而OSEventCnt的低8位保留了任务原来的优先级 
    */  

    //OSEventPtr是指向持有Mutex的任务,那么如果OSTCBCur不等于OSEventPtr,
    //说明当前任务并不是持有Mutex的任务,那post个毛线
    if (OSTCBCur != (OS_TCB *)pevent->OSEventPtr) {
        OS_EXIT_CRITICAL();
        return (OS_ERR_NOT_MUTEX_OWNER);
    }

    if (pcp != OS_PRIO_MUTEX_CEIL_DIS) {
        //若等于pcp说明持有Mutex的任务的优先级被提升过了
        if (OSTCBCur->OSTCBPrio == pcp) {
            //参数为当前任务的TCB、当前任务原先的优先级,即将当前任务的优先级恢复原先饿优先级
            OSMutex_RdyAtPrio(OSTCBCur, prio); 
        }
         /* 原先优先级为pcp的格子,已经不用,所以填充为OS_TCB_RESERVED */
        OSTCBPrioTbl[pcp] = OS_TCB_RESERVED;
    }

    /* 不等于0说明当前还有任务在等待Mutex,那么就要去唤醒它,如果是多个任务在等待Mutex,
    /* 那么唤醒的是众多任务中优先级最高的一个 */
    if (pevent->OSEventGrp != 0u) {
        // 得到在等待Mutex的其他任务中优先级最高的优先级prio
        prio                = OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);

        //将Mutex归属于在等待Mutex的其他任务中优先级最高的任务
        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;
        pevent->OSEventCnt |= prio;
        pevent->OSEventPtr  = OSTCBPrioTbl[prio]; 
        if ((pcp  != OS_PRIO_MUTEX_CEIL_DIS) &&
            (prio <= pcp)) {   //pip应该是最小的,即优先级是高的,不符合此条件返回错误信息OS_ERR_PIP_LOWER。 */ 
            OS_EXIT_CRITICAL();

            //为什么两种情况都要调用调度函数OS_Sched() ,因为这个错误并非致命,os还得继续执行
            OS_Sched();
            return (OS_ERR_PCP_LOWER);
        } else {
            OS_EXIT_CRITICAL();
            OS_Sched();
            return (OS_ERR_NONE);
        }
    }
    //执行到这里说明没有任务在等待当前任务释放的Mutex,直接走人,
    //即OSEventCnt设置为OS_MUTEX_AVAILABLE表示该Mutex闲置着
    pevent->OSEventCnt |= OS_MUTEX_AVAILABLE;
    pevent->OSEventPtr  = (void *)0;
    OS_EXIT_CRITICAL();
    return (OS_ERR_NONE);
}


6. 获取Mutex的当前状态信息函数OSMutexQuery()

  Mutex的信息存放于p_mutex_data中,函数调用者应先为其分配OS_MUTEX_DATA的数据空间。

#if OS_MUTEX_QUERY_EN > 0
INT8U  OSMutexQuery (OS_EVENT *pevent,      //指向管理某资源的互斥型信号量
                     OS_MUTEX_DATA *p_mutex_data) //输出型参数,用于存放Mutex的状态信息
{
    INT8U      i;
#if OS_LOWEST_PRIO <= 63
    INT8U     *psrc;
    INT8U     *pdest;
#else
    INT16U    *psrc;
    INT16U    *pdest;
#endif
#if OS_CRITICAL_METHOD == 3  
    OS_CPU_SR  cpu_sr = 0;
#endif

    if (OSIntNesting > 0) { 
        return (OS_ERR_QUERY_ISR);
    }
#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {   
        return (OS_ERR_PEVENT_NULL);
    }
    if (p_mutex_data == (OS_MUTEX_DATA *)0) {
        return (OS_ERR_PDATA_NULL);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {  
        return (OS_ERR_EVENT_TYPE);
    }
    OS_ENTER_CRITICAL();

    //填充输入型参数p_mutex_data
    //天花板优先级
    p_mutex_data->OSMutexPIP  = (INT8U)(pevent->OSEventCnt >> 8);  
    //持有Mutex的任务的优先级
    p_mutex_data->OSOwnerPrio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);

    如果占用mutex任务的优先级为255,表示该Mutex可用
    if (p_mutex_data->OSOwnerPrio == 0xFF) {
        p_mutex_data->OSValue = OS_TRUE; //可用
    } else {  
        p_mutex_data->OSValue = OS_FALSE; //不可用
    }

    //等待事件组
    p_mutex_data->OSEventGrp  = pevent->OSEventGrp; 
    //等待事件组的源地址
    psrc                      = &pevent->OSEventTbl[0];

    //等待事件的目的地址
    pdest                     = &p_mutex_data->OSEventTbl[0];

    //拷贝
    for (i = 0; i < OS_EVENT_TBL_SIZE; i++) {
        *pdest++ = *psrc++;
    }
    OS_EXIT_CRITICAL();
    return (OS_ERR_NONE);
}
#endif   


7. 还原任务的优先级函数OSMutex_RdyAtPrio()

  这个函数在上面函数被调用过。

static  void  OSMutex_RdyAtPrio (OS_TCB  *ptcb,
                                 INT8U    prio)
{
    // 将当前任务从就绪表中拿出,即不让当前任务为就绪态
    INT8U  y;
    y            =  ptcb->OSTCBY;          
    OSRdyTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX;
    if (OSRdyTbl[y] == 0u) {
        OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY;
    }
    //还原当前任务的优先级
    ptcb->OSTCBPrio         = prio;
    OSPrioCur               = prio; 

//优先级改变了,下面与优先级相关的参数也要做改变
#if OS_LOWEST_PRIO <= 63u
    ptcb->OSTCBY            = (INT8U)((INT8U)(prio >> 3u) & 0x07u);
    ptcb->OSTCBX            = (INT8U)(prio & 0x07u);
#else
    ptcb->OSTCBY            = (INT8U)((INT8U)(prio >> 4u) & 0x0Fu);
    ptcb->OSTCBX            = (INT8U) (prio & 0x0Fu);
#endif

//再将该任务在就绪表中标志位就绪态。因为优先级变了,那么在就绪表中对应的节点也改变了
    ptcb->OSTCBBitY         = (OS_PRIO)(1uL << ptcb->OSTCBY);
    ptcb->OSTCBBitX         = (OS_PRIO)(1uL << ptcb->OSTCBX);
    OSRdyGrp               |= ptcb->OSTCBBitY;             /* Make task ready at original priority     */
    OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
    OSTCBPrioTbl[prio]      = ptcb;
}
#endif   


版权声明:本文为CSDN博主「mybright_」的原创文章,
原文链接:https://blog.csdn.net/qq_29344757/article/details/77917033

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值