uc/os-ii 互斥信号量及mutex.c源码分析

互斥信号量:


  • 互斥信号量最主要的功能是对共享资源的互斥访问控制。是一种特殊的二值信号量,它支持所有权、递归访问、任务删除安全等概念,以及一些避免优先级反转、饥饿、死锁等互斥固有问题的解决方法。
    解决优先级反转:当高优先级任务需要使用某个共享资源,而恰巧该共享资源又被一个低优先级任务占用时,优先级反转问题就会发生。为了降解优先级反转,内核就必须支持优先级继承,将低优先级任务的优先级提升到高于高优先级任务的优先级,直到低优先级任务处理完毕共享资源。

OS_MUTEX.C中µC/OS-Ⅱ提供对互斥信号量进行管理的函数

uc/os-ii 互斥信号量及mutex.c源码分析_第1张图片

  • 事件控制块ECB结构
typedef struct {
    INT8U   OSEventType;                      /* 事件类型  */
    INT8U   OSEventGrp;                    /*事件等待组*/
    INT16U  OSEventCnt;             /* 计数器(当事件是信号量时) */
    void   *OSEventPtr;             /*空事件块时用来链接空事件链表
                               事件请求成功后指向占用任务的TCB指针*/
    INT8U   OSEventTbl[OS_EVENT_TBL_SIZE]; /*事件等待表*/
} OS_EVENT;

uc/os-ii 互斥信号量及mutex.c源码分析_第2张图片

  1. OSEventTbl[]任务组 和 OSEventGrp任务表 很像前面讲到的OSRdyTbl[]和OSRdyGrp,前两者包含的是等待某事件的任务,而后两者包含的是系统中处于就绪状态的任务。
  2. OSEventTbl[ ],作为等待事件任务的记录表,即等待任务表,该位为“1”来表示这一位对应的任务为事件的等待任务,“0”不是等待任务。(EventWaitListInit()来初始化)
  3. OSEventCnt被分成了低8位和高8位两部分:
    低8位用来存放信号值(该值为((INT16U)0x00FFu)时,信号为有效,否则信号为无效)
    高8位用来存放为了减少出现优先级反转现象而要提升的优先级别prio。

mutex中涉及的TCB

uc/os-ii 互斥信号量及mutex.c源码分析_第3张图片

  • OSTCBCur-指向“当前任务控制块”的指针;
    (当前占用cpu的任务)
    OSTCBFreeList-“空任务控制块”链表的表头指针;
    OSTCBHighRdy -指向“将要运行最高优先级任务控制块”的指针;
    OSTCBList-“已使用任务控制块”链表的表头指针;
  • OSTCBTbl[]-任务控制块数组,所有的任务控制块都保存在这个数组中。
  • 具体做法为:系统在调用函数OSInit()对ucos进行初始化时,就现在RAM中建立一个OS_TCB结构类型的数组OSTCBTbl[],然后把各个元素连接成链表,从而形成一个空的任务块链表。
    uc/os-ii 互斥信号量及mutex.c源码分析_第4张图片
    uc/os-ii 互斥信号量及mutex.c源码分析_第5张图片
  • OSTCBPrioTbl[]-任务控制块优先级表,按任务的优先级别将这些指针存放在数组的各个元素里,专门用来存放指向各任务控制块的指针。访问任务控制块时,不需要遍历任务控制块链表。
  • OSTCBPrioTbl[prio] = OS_TCB_RESERVED; /* 放置一个非空指针防止被 其他人占用(设置OSTCKPrioTbl为有效)

建立互斥信号量OSMutexCreate()

  • 1.创建一个互斥型信号量
    a.首先进行运行条件检查(函数执行参数检查,校验优先级,中断状态)
    b.检查PIP优先级是否被占用(该优先级是否有任务)
    c.检查是否有可用的空闲事件控制块

    a.b.c 都满足则建立互斥信号量
    d.取走了一个空闲ECB,调整空闲ECB链表指针
    e.紧接着将ECB设置成Mutex类型
    f.将事件控制块的计数器Cnt高8位设置为优先级,低8位全为1表示互斥信号量可用
    g.任务列表初始化完毕后,返回ECB指针。

uc/os-ii 互斥信号量及mutex.c源码分析_第6张图片

*建立一个互斥型信号量 
*描述:建立一个互斥型信号量 
*参数:prio:当存取(访问)互斥型信号量时它的优先级。换言之,当任务需要信号量, 
*                     而另一优先级更高的任务想得到信号量,就改变当前任务的优先级,变为更高 
*                     假定你改变的优先级值小于任务竞争这个信号量的任务的值(即优先级更高) 
*      Perr:指向返回应用程序的错误代码的指针: 
*                               OS_ERR_NONE          如果调用成功 
*                               OS_ERR_CREATE_ISR   如果想从ISR中建立互斥
*                               OS_PRIO_EXIST       如果优先级继承优先级的优先级已经存在(优先级继承算法或优先级天花板算法) 
*                               OS_ERR_PEVENT_NULL  没有事件控制块可用 
*                               OS_PRIO_INVALID    如果你指定的优先级大于最大值(无效优先级) 
        INT8U:8位无符号char型变量
*********************************************************************************************************
*/

OS_EVENT  *OSMutexCreate (INT8U   prio,                             //prio是取得信号量的任务需要提升到的优先级
                          INT8U  *perr)                             //用于输出错误信息 
{
    OS_EVENT  *pevent;                                          //指向事件控制块的指针(创建信号量的句柄)
#if OS_CRITICAL_METHOD == 3                                /* 为CPU状态寄存器分配存储 */
    OS_CPU_SR  cpu_sr = 0;                                  //得到当前处理器状态字的值,并将其保存在C函数局部变量之中
#endif



#if OS_ARG_CHK_EN > 0                                       //对μC/OS-III的大部分函数执行参数检查(>0)
    if (perr == (INT8U *)0) {                              /*校验 'perr'                          */
        return ((OS_EVENT *)0);
    }
    if (prio >= OS_LOWEST_PRIO) {                          /* 校验优先级,如果优先级无效                            */
        *perr = OS_ERR_PRIO_INVALID;                        //错误指向:无效优先级
        return ((OS_EVENT *)0);
    }
#endif
    if (OSIntNesting > 0) {                                /* 如果在中断中(调度器加锁)             */
        *perr = OS_ERR_CREATE_ISR;                         /* 错误指向:不允许在中断中建立互斥      */
        return ((OS_EVENT *)0);
    }
    OS_ENTER_CRITICAL();                                    /*进入临界区,关中断(保护临界区)
    if (OSTCBPrioTbl[prio] != (OS_TCB *)0) {               /* 判断需要提升到的优先级上是否有建立任务    */
        OS_EXIT_CRITICAL();                                /* 任务已优先存在,开中断,退出临界区*/
        *perr = OS_ERR_PRIO_EXIST;                         /* 错误指向:需要提升优先级已经有任务         */
        return ((OS_EVENT *)0);
    }//通过任务优先级号快速找到当前任务在任务控制块中的首地址(即OSTCBStkPtr地址)
    OSTCBPrioTbl[prio] = OS_TCB_RESERVED;                  /* 放置一个非空指针防止被其他人占用(设置OSTCKPrioTbl为有效)                  */
    pevent             = OSEventFreeList;                  /* 获取下一个空闲事件控制块       */
    if (pevent == (OS_EVENT *)0) {                         /* 如果ECB是可获取的,是可用的              */
        OSTCBPrioTbl[prio] = (OS_TCB *)0;                  /* 释放占有的优先级 ,释放表项         */
        OS_EXIT_CRITICAL();                                 //退出临界区
        *perr              = OS_ERR_PEVENT_NULL;           /* 错误指向:没有事件控制块可用             */
        return (pevent);
    }
    OSEventFreeList        = (OS_EVENT *)OSEventFreeList->OSEventPtr;   /* 取走了一个空闲ECB,调整空闲ECB链表指针       */
    OS_EXIT_CRITICAL();                                  /*开中断*/
    pevent->OSEventType    = OS_EVENT_TYPE_MUTEX;        /*设置ECB数据结构的事件类型为互斥信号量*/
    pevent->OSEventCnt     = (INT16U)((INT16U)prio << 8) | OS_MUTEX_AVAILABLE; /* 将优先级数值存在事件计数值OSEventCnt的高8位 */
                                            /*AVAILABLE的值存储在低8位,其值为0x00FF。若低八位全为1,表示该互斥信号量可以用,否则就是不可用*/
                                            /*低8位在没有任务占用时为0xFF,有任务占用时,用于保存占用任务的优先级*/
    pevent->OSEventPtr     = (void *)0;                   /*只有在所定义的事件是邮箱或者消息队列时才使用(指向下一个ECB的指针),断开与空闲链表的联系*/
#if OS_EVENT_NAME_EN > 0
    pevent->OSEventName    = (INT8U *)"?";                  /*事件名称*/
#endif
    OS_EventWaitListInit(pevent);                       /*初始化ECB的任务等待表表,全为0,没有任务在等待事件 */
    *perr                  = OS_ERR_NONE;               /*指向:调用成功(互斥信号量创建成功)
    return (pevent);                                    
}

/*$PAGE*/

等待(申请)互斥信号量OSMutexPend()

  • 描述: pevent :是一个指向所需的相关联的事件控制块的指针
    timeout:等待超时时限
    如果非0,您的任务将等待资源达到该参数指定的时间量。
    如果为0,任务将永远在指定的位置等待互斥信号量,直到资源可用为止。
    perr : 指向错误代码 OS_ERR_NONE 调用成功,任务拥有互斥锁。
    OS_ERR_TIMEOUT 互斥锁在指定的“超时”内不可用。
    OS_ERR_PEND_ABORT 互斥锁上的等待被异常中止。.
    OS_ERR_EVENT_TYPE ‘pevent’不是一个指向互斥信号量
    OS_ERR_PEVENT_NULL ‘pevent’ 是空指针
    OS_ERR_PEND_ISR ISR调用这个函数时错误.
    OS_ERR_PIP_LOWER 申请任务的优先级高于继承优先级(互斥机制失效)
    OS_ERR_PEND_LOCKED 调度器上锁不能等待信号量
  • 操作流程:
    a.进行运行条件检查
    b.足运行条件后,首先检查是否有Mutex可用,如果有可用的Mutex,则占用这个Mutex,调用者获得共享资源的使用权;
    c.如果没有可用的Mutex,则需要检查Mutex的占用者是否需要提升优先级;(从ECB中取得占用的TCB指针和优先级,判断只有当占用ECB的优先级比升级优先级和当前请求优先级都低的时候才进行升级,)
    d.对需要提升优先级的占用者进行一系列处理;
    对占用任务判定是否就绪,就绪就更新新优先级的就绪组和就绪表,未就绪则更新ECB的等待数和等待表
    f.挂起调用者,并调用调度函数切换最高优先级任务;
    g.当调用者再次运行时,需要检查调用者是因为超时还是得到Mutex而运行的,并作相应处理。
#define  OS_MUTEX_KEEP_LOWER_8   ((INT16U)0x00FFu)   /*设置低8位全为1*/
#define  OS_MUTEX_KEEP_UPPER_8   ((INT16U)0xFF00u)   /*设置高8位全为1*/
#define  OS_MUTEX_AVAILABLE      ((INT16U)0x00FFu)
    INT8U      pip;                                              /* 优先级继承优先级 (PIP)      */

      注:Pip变量是保存在OSEventCnt中的,当我们创建信号量的时候,就会给定这个值,  这个值也就是系统能够将等待该互斥信号量的任务提升的最高优先级


    INT8U      mprio;                                       /* 占用信号量的任务自己的优先级         */
    BOOLEAN    rdy;                                          /* Flag indicating task was ready 任务已就绪标志    */
    OS_TCB    *ptcb;                                             /*任务控制块结构体(占用互斥信号量的控制块)*/
    OS_EVENT  *pevent2;                                     /*指向任务未就绪时等待事件的指针*/
    INT8U      y;               /*优先级高3位对应的数值*/


#if OS_CRITICAL_METHOD == 3                                /* 为CPU状态寄存器分配存储 */
    OS_CPU_SR  cpu_sr = 0;
#endif


/*进行运行条件检查*/
#if OS_ARG_CHK_EN > 0                                       /*对μC/OS-III的大部分函数执行参数检查(>0)
    if (perr == (INT8U *)0) {                               /* 校验 'perr'                          */
        return;
    }
    if (pevent == (OS_EVENT *)0) {                          /* 校验 'pevent'                        */
        *perr = OS_ERR_PEVENT_NULL;                         /*错误指向:空指针
        return;
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {       /* 校验事件控制块(互斥信号量)                */
        *perr = OS_ERR_EVENT_TYPE;                          /*错误指向:不是互斥信号量类型
        return;
    }
    if (OSIntNesting > 0) {                                /* 如果在中断               */
        *perr = OS_ERR_PEND_ISR;                           /* 错误指向:ISR调用这个函数和结果将导致阻塞               */
        return;
    }
    if (OSLockNesting > 0) {                               /*如果调度程序锁定   */
        *perr = OS_ERR_PEND_LOCKED;                        /*错误指向:调度任务锁住阻塞              */
        return;
    }
/*$PAGE*/
   OS_ENTER_CRITICAL();             /*进入临界区*/
    pip = (INT8U)(pevent->OSEventCnt >> 8);                  /* Get PIP from mutex 事件计数器高8位保存PIP */
                                                                     /* Is Mutex available?                      */
    if ((INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) 
    {           /*提取低8位,看是否全为1,来判断互斥信号量是否可用。*/
        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;      
                         /* 修改高8位的数据,这个时候高8位数据不变,
                        还是表示继承优先级,低8位清零。               */
        pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;        
                        /* 将申请任务的优先级保存在低8位之中。低八位不可能全                        部都为零,所以依旧可以表示信号量是否被占用。*/

  pevent->OSEventPtr  = (void *)OSTCBCur;                  /* OSEventPtr指针指向当前占用信号的任务控制块*/
                                    /*获取mutex*/
        if (OSTCBCur->OSTCBPrio <= pip) {                 
                          /*申请信号量任务的优先级高于继承优先级(数值上小于)*/
            OS_EXIT_CRITICAL();                                                                        /*退出临界区 */
            *perr = OS_ERR_PIP_LOWER;                /*错误指向:申请优先级高于继承优先级的优先级*/  
                    /*不需要提高优先级*/ 

      } else {                    /*申请任务的优先级高于继承优先级的优先级*/
            OS_EXIT_CRITICAL();
            *perr = OS_ERR_NONE;                                           /*指向:调用成功*/
        }
        return;
    }

    mprio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);  
                       /*取得互斥信号量任务的优先级(因为被占用,所以低8位为优先级)*/
    ptcb  = (OS_TCB *)(pevent->OSEventPtr);                         /*从信号量中获得占用该信号量的任务*/

    if (ptcb->OSTCBPrio > pip) {                    /*占用的任务优先级低于最高继承优先级*/                        

        if (mprio > OSTCBCur->OSTCBPrio) {  
                               /*且原先占用该mutex的优先级比提出申请该mutex的任务                          的优先级低则提升原任务的优先级至PIP*/
            y = ptcb->OSTCBY;           /*优先级高3位对应的数值(=prio>>3)*/
            if ((OSRdyTbl[y] & ptcb->OSTCBBitX) != 0) {      /* 看取得信号量的任务是否就绪 (对应就绪表的值)  */
                OSRdyTbl[y] &= ~ptcb->OSTCBBitX;                                        /*   若原任务已就绪,则将其从就                                  绪表中删除,并置就绪标志rdy*/ 
                if (OSRdyTbl[y] == 0) {                                                           
                    OSRdyGrp &= ~ptcb->OSTCBBitY;
                }
                rdy = OS_TRUE;
                } else {//若没有就绪,在等待一个事件

  pevent2 = ptcb->OSTCBEventPtr;                    /*指向未就绪任务等待的事件*/
              if (pevent2 != (OS_EVENT *)0) {                        /* 任务存在等待事件发生       */
                    if ((pevent2->OSEventTbl[ptcb->OSTCBY] &= ~ptcb->OSTCBBitX) == 0) {
                        pevent2->OSEventGrp &= ~ptcb->OSTCBBitY;           /*清除对应的等待表和等待组*/
                    }
                }
                rdy = OS_FALSE;                                           /* 标记任务未就绪  */
     }
            ptcb->OSTCBPrio = pip;                                          /* 提升占用信号的优先级至pip         */
             /*根据新取得的优先级,修改优先级,更新任务控制块的相关数据。*/

/*OSRdyGrp |= OSMapTbl[prio >> 3];  原先的查找表设置
OSRdyTbl[prio >> 3] |= OSMapTbl[prio & 0x07];*/
//不查表,直接把0x01左移动 OSRdyTbl[]元素即可

#if OS_LOWEST_PRIO <= 63    /*改进*/
            ptcb->OSTCBY    = (INT8U)( ptcb->OSTCBPrio >> 3);            /*优先级的高三位*/
            ptcb->OSTCBX    = (INT8U)( ptcb->OSTCBPrio & 0x07); /*优先级的低三位*/
            ptcb->OSTCBBitY = (INT8U)(1u << ptcb->OSTCBY);            /*优先级在就绪组的位置*/
            ptcb->OSTCBBitX = (INT8U)(1u << ptcb->OSTCBX);      /*优先级在就绪表的位置*/
#else
            ptcb->OSTCBY    = (INT8U)((ptcb->OSTCBPrio >> 4) & 0xFF);
            ptcb->OSTCBX    = (INT8U)( ptcb->OSTCBPrio & 0x0F);
            ptcb->OSTCBBitY = (INT16U)(1u << ptcb->OSTCBY);
            ptcb->OSTCBBitX = (INT16U)(1u << ptcb->OSTCBX);
#endif
if (rdy == OS_TRUE) {                      /* 如果取得信号量的任务为就绪状态,则继续让新的优先级就绪*/
                OSRdyGrp               |= ptcb->OSTCBBitY; /* 更新就绪数组和就绪表(任务的新优先级)    */
                OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
            } else {//如果不是就绪
                pevent2 = ptcb->OSTCBEventPtr;
                if (pevent2 != (OS_EVENT *)0) {            /* 将其更新ECB的等待数组和等待表(Add to event                                         wait list) */
                    pevent2->OSEventGrp               |= ptcb->OSTCBBitY;
                    pevent2->OSEventTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
                }
            }
            OSTCBPrioTbl[pip] = ptcb;           /*优先级数组指针指向取得的任务控制块*/
        }
    }
    //到这说明前面已经把占用信号量任务的优先级提高
        //当前任务TCB域处理:

    OSTCBCur->OSTCBStat     |= OS_STAT_MUTEX;    /*#define  OS_STAT_MUTEX ox10u */    
                        /* 将当前任务的状态设为等待互斥信号量*/
    OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;/* 事件等待标志:事件发生,等待结                                               束*/
    OSTCBCur->OSTCBDly       = timeout;                   /* 在任务控制块设置延时时间*/

    OS_EventTaskWait(pevent);                         /* 取消申请任务的的就绪状态,设置等待事件                          阻塞,直到时间超时或者是事件发生  */
        OS_EXIT_CRITICAL();
    OS_Sched();                                             /* 执行调度,运行最高优先级的就绪任务
            如果原来低优先级的任务优先级被抬高了,则该任务将被执行 */
    OS_ENTER_CRITICAL();
    //原先占用mutex的任务执行完成释放了mutex,并唤醒了等待该mutex的最高优先级的任务
    switch (OSTCBCur->OSTCBStatPend) {                /* 检查调用者是因为超时还是得到Mutex而运行*/
        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:
             OS_EventTaskRemove(OSTCBCur, pevent);      /*将该任务从该等待事件中取消*/
             *perr = OS_ERR_TIMEOUT;                            /* 时间超时没有得到互斥信号量   */
             break;
    }
    OSTCBCur->OSTCBStat          =  OS_STAT_RDY;      /*   任务当前状态               */
                         /*#define OS_STAT_RDY  ox00u */
    OSTCBCur->OSTCBStatPend      =  OS_STAT_PEND_OK;  /*等待事件发生,等待结束*/

    OSTCBCur->OSTCBEventPtr      = (OS_EVENT  *)0;           /* 清除事件控制块指针*/
                        /*表示当前任务没有事件阻塞*/
/*EVENT_MULTI_EN表示等待多个事件*/
#if (OS_EVENT_MULTI_EN > 0)

    OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;      /*清除指向多事件控制块指针*/

#endif

    OS_EXIT_CRITICAL();
}

恢复任务的优先级RdyAtPrio ()

  • 描述:
    当释放信号量的任务的优先级为等于互斥信号量最高可提升优先级的时候,
    需要通过OSMutex_RdyAtPrio函数来将任务的优先级恢复

         功能是将ptcb指向的任务控制块的任务的优先级改为prio并且就绪 
    
  • 操作流程:
    a.让升级了的任务优先级恢复原优先级并使之就绪,首先清0这个任务在就绪表、组,然后获取TCB中的得到相关域。
    b.就绪表、组标记为原优先级的就绪状态,优先级指针表存TCB *。
    参数: INT8U y;
static  void  OSMutex_RdyAtPrio (OS_TCB  *ptcb, 
                                 INT8U    prio)
{
    y            =  ptcb->OSTCBY;                       /* 获取任务就绪表OSRdyGrp高三位*/   
   /* 将提升到高优先级从就绪表和就绪数组中清除   */
    OSRdyTbl[y] &= ~ptcb->OSTCBBitX;   /*清除就绪表*/       
    if (OSRdyTbl[y] == 0) {//为0对应       /*清除就绪组*/
        OSRdyGrp &= ~ptcb->OSTCBBitY;
    }
    ptcb->OSTCBPrio         = prio;     /*将任务控制块的任务优先级恢复到prio*/
/*更新TCB任务控制块的跟优先级相关的数据成员*/
#if OS_LOWEST_PRIO <= 63

    ptcb->OSTCBY            = (INT8U)((prio >> (INT8U)3) & (INT8U)0x07);
    ptcb->OSTCBX            = (INT8U) (prio & (INT8U)0x07);
    ptcb->OSTCBBitY         = (INT8U)(1u << ptcb->OSTCBY);
    ptcb->OSTCBBitX         = (INT8U)(1u << ptcb->OSTCBX);
#else
    ptcb->OSTCBY            = (INT8U)((prio >> (INT8U)4) & (INT8U)0x0F);
    ptcb->OSTCBX            = (INT8U) (prio & (INT8U)0x0F);
    ptcb->OSTCBBitY         = (INT16U)(1u << ptcb->OSTCBY);
    ptcb->OSTCBBitX         = (INT16U)(1u << ptcb->OSTCBX);


#endif
    OSRdyGrp               |= ptcb->OSTCBBitY;               /* 更新就绪组     */
    OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;              
                                /*更新就绪表*/
    OSTCBPrioTbl[prio]      = ptcb;                         
                /*将对应优先级的数组指正指向该任务控制块*/
    }


#endif                                                     /* OS_MUTEX_EN                              */

无等待的获取互斥信号量(非阻塞OSMutexAccept ()

  • 参数: pevent 指向任务控制块的指针
    perr 一个指向一个错误代码将返回到您的应用程序:
    os_err_none 如果调用成功。
    os_err_event_type ‘pevent’不是一个指向互斥信号量
    os_err_pevent_null ‘pevent’是一个空指针
    os_err_pend_isr 如果从ISR调用这个函数
    os_err_pip_lower 申请任务的优先级比继承优先级小
  • Returns : == OS_TRUE 如果信号量可用,互斥信号量可采集
    == OS_FALSE 如果信号量不可用

  • 操作流程:
    a.进行运行条件检查(函数执行参数检查,验证事件块类型,中断状态)
    b.关中段保护临界区
    c.获取事件计数器高8位存放pip
    d.判断事件计数器低8位数值看信号量是否可用
    f.如果可用,到8位保存pip,低8位保存任务的优先级
    g.任务获得信号量

#if OS_MUTEX_ACCEPT_EN > 0
BOOLEAN  OSMutexAccept (OS_EVENT  *pevent,                    //非阻塞的获取互斥型信号量函数
                        INT8U     *perr)
{
     INT8U      pip;                                           /* 优先级继承优先级 (PIP)          */
#if OS_CRITICAL_METHOD == 3                            /* 为CPU状态寄存器分配存储      */
    OS_CPU_SR  cpu_sr = 0;
#endif

#if OS_ARG_CHK_EN > 0       /*对μC/OS-III的大部分函数执行参数检查(>0)*/

    if (perr == (INT8U *)0) {                             /* 验证 'perr'  */
        return (OS_FALSE);          /*perr为null返回false*/
    }

    if (pevent == (OS_EVENT *)0) {                     /* 验证 'pevent' */
        *perr = OS_ERR_PEVENT_NULL;     /*错误指向:'pevent'是一个空指针*/
        return (OS_FALSE);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {  
                        /* 验证事件块类型(不为互斥信号类型)*/

        *perr = OS_ERR_EVENT_TYPE;      /*错误指向:perr 不是一个指向互斥信号量*/
        return (OS_FALSE);
    }

    if (OSIntNesting > 0) {                                    /*确保它不是从ISR调用的    */
        *perr = OS_ERR_PEND_ISR;            /*错误指向:从ISR调用这个函数*/
        return (OS_FALSE);  
    }
    OS_ENTER_CRITICAL();                                      /* Get value (0 or 1) of Mutex  关中断(保护临界区*/

    pip = (INT8U)(pevent->OSEventCnt >> 8);            /* Get PIP from mutex    事件计数器高8位保存PIP  */
    if ((pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8) == OS_MUTEX_AVAILABLE) {
                            /*提取低8位,看是否全为1,来判断互斥信号量是否可用*/

       pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;  
                         /*  修改高8位的数据,这个时候高8位数据不变,
                            还是表示继承优先级,低8位清零。        */

        pevent->OSEventCnt |= OSTCBCur->OSTCBPrio;    
                             /* 将申请任务的优先级保存在低8位之中。
                  低八位不可能全部都为零,所以依旧可以表示信号量是否被占用。    */
        pevent->OSEventPtr  = (void *)OSTCBCur;       
                             /* OSEventPtr指针指向任务控制块         */
                                /*把任务占用信号量*/
                                    if (OSTCBCur->OSTCBPrio <= pip) {              /* 申请任务优先级高于继承优先级的数值。*/
            OS_EXIT_CRITICAL();                               /*   开中断                */
            *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);
     }
#endif

查询互斥信号量信息 OSMutexQuery ()

  • 功能:查询互斥信号量信息
    参数: pevent 是一个指向所需的互斥体相关的事件控制块。
    p_mutex_data 是一个指向一个数据结构,包含了互斥信息返回:

    OS_ERR_NONE 调用成功
    OS_ERR_QUERY_ISR If you called this function from an ISR
    OS_ERR_PEVENT_NULL If ‘pevent’ is a NULL pointer
    OS_ERR_PDATA_NULL ‘p_mutex_data’ 空指针
    OS_ERR_EVENT_TYPE 非互斥信号量类型获取数据错误

  • 操作流程:
    a.进行运行条件检查()
    b.p_mutex_data结构来存放互斥信号量的信息
    c.获取高八位优先继承优先和低八位任务优先级
    d.拷贝任务等待信号量表到状态数据结构中
#if OS_MUTEX_QUERY_EN > 0
INT8U  OSMutexQuery (OS_EVENT       *pevent,                    /*一个指向所需互斥体相关的事件控制块*/
                     OS_MUTEX_DATA  *p_mutex_data)              /*包换互斥信息的结构体
{
    INT8U      i;
#if OS_LOWEST_PRIO <= 63                                    /*优先级*/
    INT8U     *psrc;                                /*定义8位pevent->OSEventTbl[0]的地址指针*/
    INT8U     *pdest;                               /*定义8位pdata->OSEventTbl[0]的地址指针*/
#else
    INT16U    *psrc;
    INT16U    *pdest;
#endif
#if OS_CRITICAL_METHOD == 3                      /* 为CPU状态寄存器分配存储         */
    OS_CPU_SR  cpu_sr = 0;
#endif



    if (OSIntNesting > 0) {                                /* 如果在中断中             */
        return (OS_ERR_QUERY_ISR);                         /* 不能在中断中获取互斥信号量的信息      */
    }
#if OS_ARG_CHK_EN > 0                                       /*对μC/OS-III的大部分函数执行参数检查(>0)
    if (pevent == (OS_EVENT *)0) {                         /* 校验 'pevent'                        */
        return (OS_ERR_PEVENT_NULL);
    }
    if (p_mutex_data == (OS_MUTEX_DATA *)0) {              /* 校验 'p_mutex_data'                  */
        return (OS_ERR_PDATA_NULL);                         /*错误指向:p_mutex_data 为空
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {      /* 校验事件块类型            */
        return (OS_ERR_EVENT_TYPE);                         /*返回错误:不是互斥信号量类型
    }   
    OS_ENTER_CRITICAL();                                    /*关中断(保护临界区)
    p_mutex_data->OSMutexPIP  = (INT8U)(pevent->OSEventCnt >> 8); /*高8位的数据,即继承优先级的数值*/
    p_mutex_data->OSOwnerPrio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);/*低八位的数据,信号量拥有的优先级
    if (p_mutex_data->OSOwnerPrio == 0xFF) {                /*若低八位都为1   
        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                                                     /* OS_MUTEX_QUERY_EN*/

释放互斥信号量OSMutexPost ()

  • 参数 : pevent 是一个指向所需的相关联的事件控制块的指针

返回 : OS_ERR_NONE 调用成功
OS_ERR_EVENT_TYPE ‘pevent’不是一个指向互斥信号量类型的指针
OS_ERR_PEVENT_NULL ‘pevent’是一个空指针
OS_ERR_POST_ISR 中断时不能释放互斥信号量
OS_ERR_NOT_MUTEX_OWNER 完成该任务的任务不是互斥信号量的所有者
OS_ERR_PIP_LOWER 申请任务的优先级高于继承优先级

  • 操作流程:

a.进行运行条件检查(中断检测,校验’pevent’,校验事件块类型 )
b.如果任务不是互斥信号量的所有者任务,则报错
c.高优先级任务想得到Mutex时,如果Mutex占用者的优先级已经被升高,那么调用OSMutex_RdyAtPrio()函数使优先级升高了的任务恢复原来的优先级。

d.如果有多个任务在等待一个Mutex,那么其中优先级最高的任务获得Mutex,设为就绪
e.如果没有任务等待,低8位赋值为OS_MUTEX_AVAILABLE,即信号量的状态为可用,OSEventPtr=0,占用的任务为空。

INT8U  OSMutexPost (OS_EVENT *pevent)
{
    INT8U      pip;                                   /* 优先级继承优先级                */
    INT8U      prio;            
#if OS_CRITICAL_METHOD == 3                           /* 为CPU状态寄存器分配存储      */
    OS_CPU_SR  cpu_sr = 0;
#endif

    if (OSIntNesting > 0) {                                     /* 如果在中断中 */
        return (OS_ERR_POST_ISR);                     /* 错误返回:不能在中断中释放互斥信号量 */
    }
#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {                             /* 校验'pevent  */
        return (OS_ERR_PEVENT_NULL);                  /*错误指向:'pevent'是个空指针
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) { /* 校验事件块类型 (不是互斥信号量类型)                   */
        return (OS_ERR_EVENT_TYPE);                 /*错误指向:不是互斥信号量类型
    }

    OS_ENTER_CRITICAL();                    /*关中断(保护临界区)
    pip  = (INT8U)(pevent->OSEventCnt >> 8);          /* 事件计数器高八位取得优先级的继承优先级数值(PIP)*/
    prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);  
                           /* 提取低8位,取得信号量任务的原先优先级     */
    if (OSTCBCur != (OS_TCB *)pevent->OSEventPtr) {         
                          /* 判断是提交信号量的任务是不是取得信号量的任务  */
        OS_EXIT_CRITICAL();                 /*开中断*/
        return (OS_ERR_NOT_MUTEX_OWNER);    /*错误指向:该任务不是互斥信号量的所有者任务*/
    }
    if (OSTCBCur->OSTCBPrio == pip) {                                      /* 如果取得信号量的任务的优先级等于继承优先级 */
                            /*判定已经优先级反转过*/
        OSMutex_RdyAtPrio(OSTCBCur, prio);                             /* 恢复取得信号量之前的优先级          */
    }
    OSTCBPrioTbl[pip] = OS_TCB_RESERVED;             /* 将pip优先级对应的任务优先级数组指针赋值                           为OS_TCB_RESERVED (占用)*/           
    if (pevent->OSEventGrp != 0) {                          /* 有等待任务时               */
         prio= OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);
                                                                                   /*将等待信号量的最高优先级任务的设为就绪*/
        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;  /* 高8位保存 pip(继承优先级)      */
        pevent->OSEventCnt |= prio;             /*低八位保存刚刚转为就绪任务的优先级*/
        pevent->OSEventPtr  = OSTCBPrioTbl[prio];       
                                           /*对应优先级的任务控制块获得互斥信号*/
        if (prio <= pip) {                            /* 刚刚转为就绪任务的优先级高于继承优先级,mutex机制已经不起作用       */
            OS_EXIT_CRITICAL();                   /*开中断     
            OS_Sched();                                 /*  调度指向优先级最高的任务 */
            return (OS_ERR_PIP_LOWER);      /*返回指向:任务优先级高于pip,mutex机制已经不起作用,会发生                 优先级反转*/
        } 
        else {/*内定的最高优先级pip务必大于所有能访问互斥资源的进程优先级*/
            OS_EXIT_CRITICAL();
            OS_Sched();                               /*  调度指向优先级最高的任务  */
            return (OS_ERR_NONE);    /*指向:调用成功        }
    }
    pevent->OSEventCnt |= OS_MUTEX_AVAILABLE;      
                /* 如果没有任务等待信号量,则将低8位赋值为OS_MUTEX_AVAILABLE,即信号量的状态为可用  */                               
    pevent->OSEventPtr  = (void *)0;                /*设置互斥信号量没有被任务占用*/
    OS_EXIT_CRITICAL();                  /*开中断*/
    return (OS_ERR_NONE);           /*指向:调用成功       */  
}

删除互斥信号量 OSMutexDel ()

  • 描述:该函数删除一个互斥信号量和准备所有的任务在它之前。
    参数: pervent: 是一个指向所需的互斥体相关的事件控制块。

    opt:
    opt == OS_DEL_NO_PEND 当没有任务等待时才删除互斥项。
    opt == OS_DEL_ALWAYS 即使任务正在等待,也会删除互斥体。

    perr :
                 os_err_none        调用是成功和互斥锁被删除
                 os_err_del_isr     从ISR删除互斥错误
                 os_err_invalid_opt 无效选项指定
                 os_err_task_waiting一个或多个任务等待互斥
                 os_err_event_type  'pevent'不是一个指向互斥信号量
                 os_err_pevent_null  '事件'是一个空指针。
    
  • 操作流程:

    a.首先进行运行条件检查,若不满足,则返回空指针和错误代码(函数执行参数检查,校验事件块类型,中断状态)
    b.查看是否有等待互斥信号量的任务(判断并标记是否有任务因该信号量阻塞),设置对应标志(OSEventTbl[ ],作为等待事件任务的记录表,即等待任务表,该位为“1”来表示这一位对应的任务为事件的等待任务,“0”不是等待任务。
    c.按删除条件选择参数opt进行删除
    (1)只有在没有任务等待时才删除互斥锁
    a.初始化mutex
    b.Mutex的ECB指针交还给空闲ECB链表
    (2)总是删除互斥锁
    b.初始化mutex
    a.如果发生了提升优先级,通过OSMutex_RdyAtPro(ptcb,prio)恢复为prio优先级
    c.阻塞的任务让所有因该事件被阻塞的任务全部就绪
    d升级过优先级的OSTCBPrioTbl[]的TCB指针清0,然后设置ECB的域,返回 给ECB空闲链表,调用OSSched()
    (3)无效选项指定(报错break)

#if OS_MUTEX_DEL_EN
OS_EVENT  *OSMutexDel (OS_EVENT  *pevent,               //指向事件控制块的指针(创建信号量的句柄)
                       INT8U      opt,                  //删除条件选择参数opt
                       INT8U     *perr)                 //返回应用程序的错误代码的指针
{
BOOLEAN    tasks_waiting;                                                              /*标记是否有等待互斥信号量的任务*/
OS_EVENT  *pevent_return;                                                       /*标记互斥信号量删除标志*/
    INT8U      pip;                                                          /* 优先级继承优先级            */
    INT8U      prio;                                                         /*还没优先级反转前的优先级(原优先级)*/
OS_TCB    *ptcb;                /*任务控制块*/



#if OS_CRITICAL_METHOD == 3                                /* 为CPU状态寄存器分配存储 */
    OS_CPU_SR  cpu_sr = 0;                                  //得到当前处理器状态字的值,并将其保存在C函数局部变量之中
#endif
#if OS_ARG_CHK_EN > 0               /*对μC/OS-III的大部分函数执行参数检查(>0)
    if (perr == (INT8U *)0) {                                   /* 校验'perr'                          */
        return (pevent);
    }
    if (pevent == (OS_EVENT *)0) {                                    /* 校验 'pevent'  */
        *perr = OS_ERR_PEVENT_NULL;         /*错误指向:‘事件’是一个空指针*/
        return (pevent);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {           /* 校验事件块类型 */
        *perr = OS_ERR_EVENT_TYPE;               /*'pevent'不是一个指向互斥信号量*/
        return (pevent);
    }
    if (OSIntNesting > 0) {                                         /* 如果在中断 */
        *perr = OS_ERR_DEL_ISR;                                     /* 错误指向:从ISR删除互斥错误            */
        return (pevent);
    }
    OS_ENTER_CRITICAL();            /*进入临界区,关中断(保护临界区)*/                                                
    if (pevent->OSEventGrp != 0) {                         /* 查看是否有等待互斥信号的任务      */
                        /*OSEventTbl[ ],作为等待事件任务的记录表,即等待任务表,该位为“1”来表示这一位对应的任务为事件的等待任务,“0”不是等待任务。OSEventGrp,来表示等待任务表中的任务组。*/

        tasks_waiting = OS_TRUE;                       /* Yes      标记是有等待互斥信号量的任务*/               
    } else {
        tasks_waiting = OS_FALSE;                      /* No      标记是无等待互斥信号量的任务*/                                
    }
        switch (opt) {                              /*删除的参数条件*/
        case OS_DEL_NO_PEND:                                        /* 只有在没有任务等待时才删除互斥锁 --- */
             if (tasks_waiting == OS_FALSE) {
#if OS_EVENT_NAME_EN > 0
                 pevent->OSEventName = (INT8U *)"?";                        /*事件名称*/
#endif
                 pip                 = (INT8U)(pevent->OSEventCnt >> 8);        /*事件计数器高8位,取继承优先级的数值*/
                 OSTCBPrioTbl[pip]   = (OS_TCB *)0;        /*根据继承优先级查找到任务控制表的首地址,设置OSTCKPrioTbl*/                           pevent->OSEventType = OS_EVENT_TYPE_UNUSED;     /*设置ECB数据结构的事件类型为未设置0*/
                 pevent->OSEventPtr  = OSEventFreeList;            /*指向下一个ECB为空事件控制链表 */
                 pevent->OSEventCnt  = 0;                   /*初始化ECB计数器*/
                 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:                                            /* 总是删除互斥锁 ---------------- */
             pip  = (INT8U)(pevent->OSEventCnt >> 8);                     /* 从互斥信号量中获取优先继承优先的数值*/
             prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);  /* 获取任务的最初优先级,获取取得信号量优先级的任务 */
             ptcb = (OS_TCB *)pevent->OSEventPtr;           /*获取占用该信号的TCB控制块*/
             if (ptcb != (OS_TCB *)0) {                     /* 查看是否有任何任务拥有互斥锁,看是否ECB的任务控制块指针是否为空          */
                 if (ptcb->OSTCBPrio == pip) {                       /*如果提升过优先级        */
                     OSMutex_RdyAtPrio(ptcb, prio);                    /*恢复之前任务的优先级   */
                 }
             }
             while (pevent->OSEventGrp != 0) {                        /* 遍历互斥信号量的所有等待任务        */
                 (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);
                    /*选择pevent中优先级最高的任务离开等待队列,进入就绪队列*/
             }
#if OS_EVENT_NAME_EN > 0
             pevent->OSEventName = (INT8U *)"?";
#endif
    pip       = (INT8U)(pevent->OSEventCnt >> 8);
             OSTCBPrioTbl[pip]   = (OS_TCB *)0;                 /*对应优先级 TCB指针清空,清除占用 */
             pevent->OSEventType = OS_EVENT_TYPE_UNUSED;    /*设置ECB数据结构的事件类型为未设置*/
             pevent->OSEventPtr  = OSEventFreeList;          /*把该事件控制块返回给空事件控制链表 */
             pevent->OSEventCnt  = 0;           /*初始化ECB计数器*/
             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);                     /*返回该事件控制块的指针*/
}

你可能感兴趣的:(uCOS-II系统)