UCOSII学习之路2 任务同步之信号量的使用

来源:互联网 发布:网络上的聊污什么意思 编辑:程序博客网 时间:2024/06/06 07:00

    注:以下讨论都是基于UCOSII V2.92.07,其他版本可能略微存在差异。

引用任哲书上的一句话解释什么是信号量最合适不过,应用程序中的各个人物,必须通过彼此之间的有效合作,才能完成一项大规模的工作,因为这些任务在运行的时候,经常需要无冲突的访问同一个共享资源,或者需要互相支持和依赖,甚至有时还要相互制约才能保证任务的顺利运行,因此操作系统必须具有对任务运行进行协调的能力,从而使任务无冲突、流畅的同步运行,这就是为什么我们要使用任务间同步机制。

谈到任务的同步,UCOSII中一共有4种同步方式。(信号量、互斥信号量、消息邮箱、消息队列)而要想深入理解这一系列的机制。那免不了说说他们的基本组成单元(事件控制块),我们通过几幅图片来理解一下。


通过上面的图片来理解信号量是不是比较直观。不过开始也就让大家有个印象,接下来说说任务同步的基本组成-事件控制块。

事件控制块:
事件控制块的基本数据结构


typedef struct os_event {    INT8U    OSEventType;       //说明具体的同步机制类型,如消息邮箱                void    *OSEventPtr;        //未使用时用于链接控制块为单向链表,使用时根据具体情况使用    INT16U   OSEventCnt;            OS_PRIO  OSEventGrp;          //事件组               OS_PRIO  OSEventTbl[OS_EVENT_TBL_SIZE]; //事件标志,主要存放等待相应信号的任务标志 #if OS_EVENT_NAME_EN > 0u    INT8U   *OSEventName; //事件名#endif} OS_EVENT;

 

而在一开始,事件控制块会被系统初始化为一个单向的链表。


上面就是一个基本的事件控制块结构,他是组成其他同步机制的基础。而关于其基本的操作函数,也

有3个。
分别为
void  OS_EventWaitListInit (OS_EVENT *pevent); //初始化一个事件控制块
void  OS_EventTaskWait (OS_EVENT *pevent);   //使一个任务进入等待某事件发生的状态
INT8U  OS_EventTaskRdy (OS_EVENT  *pevent, void      *pmsg,
                        INT8U      msk, INT8U      pend_stat); //使一个任务进入就绪态
  //  void OS_EventTO(OS_EVENT *pevent); //等待超时而将任务置于就绪态 在V2.53版本中存在
void  OS_EventTaskRemove (OS_TCB   *ptcb, OS_EVENT *pevent) //V2.9版本,其作用和OS_EventTO()相同
    
    接下来我们来分析分析这四个函数的具体实现

void  OS_EventWaitListInit (OS_EVENT *pevent){    INT8U  i;    pevent->OSEventGrp = 0u;                    //初始化传进来的时间控制块OSEventGrp域为0    for (i = 0u; i < OS_EVENT_TBL_SIZE; i++) {        pevent->OSEventTbl[i] = 0u;     //初始化对应的标志数组    }}
总而言之是对OS_EVENT事件控制块域的初始化。

void  OS_EventTaskWait (OS_EVENT *pevent){    INT8U  y;    OSTCBCur->OSTCBEventPtr               = pevent;  //保存任务的事件控制块指针          pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX;//置起当前任务到等待事件表    pevent->OSEventGrp                   |= OSTCBCur->OSTCBBitY;    y             =  OSTCBCur->OSTCBY;            /* Task no longer ready                              */    OSRdyTbl[y]  &= (OS_PRIO)~OSTCBCur->OSTCBBitX;    if (OSRdyTbl[y] == 0u) {                      //如果任务没有就绪,则清除标志组        OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY;    }}
其主要是将调用本函数的任务挂起,具体挂起的类型主要看该控制块的数据类型

INT8U  OS_EventTaskRdy (OS_EVENT  *pevent,                        void      *pmsg,                        INT8U      msk,                        INT8U      pend_stat){    OS_TCB   *ptcb;    INT8U     y;    INT8U     x;    INT8U     prio;#if OS_LOWEST_PRIO > 63u    OS_PRIO  *ptbl;#endif#if OS_LOWEST_PRIO <= 63u    y    = OSUnMapTbl[pevent->OSEventGrp];              /* Find HPT waiting for message                */    x    = OSUnMapTbl[pevent->OSEventTbl[y]];    prio = (INT8U)((y << 3u) + x);                 //获取当前挂起的最高优先级任务#else    if ((pevent->OSEventGrp & 0xFFu) != 0u) {       //当最低优先级大于63时,启用16*16事件标志组        y = OSUnMapTbl[ pevent->OSEventGrp & 0xFFu];    } else {        y = OSUnMapTbl[(OS_PRIO)(pevent->OSEventGrp >> 8u) & 0xFFu] + 8u;    }    ptbl = &pevent->OSEventTbl[y];    if ((*ptbl & 0xFFu) != 0u) {        x = OSUnMapTbl[*ptbl & 0xFFu];    } else {        x = OSUnMapTbl[(OS_PRIO)(*ptbl >> 8u) & 0xFFu] + 8u;    }    prio = (INT8U)((y << 4u) + x);                      /* Find priority of task getting the msg       */#endif    ptcb                  =  OSTCBPrioTbl[prio];        /* Point to this task's OS_TCB                 */    ptcb->OSTCBDly        =  0u;                        /* Prevent OSTimeTick() from readying task     */#if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u)    ptcb->OSTCBMsg        =  pmsg;                      /* Send message directly to waiting task       */#else    pmsg                  =  pmsg;                      /* Prevent compiler warning if not used        */#endif    ptcb->OSTCBStat      &= (INT8U)~msk;                /* Clear bit associated with event type        */    ptcb->OSTCBStatPend   =  pend_stat;                 /* Set pend status of post or abort            */                                                        /* See if task is ready (could be susp'd)      */    if ((ptcb->OSTCBStat &   OS_STAT_SUSPEND) == OS_STAT_RDY) {        OSRdyGrp         |=  ptcb->OSTCBBitY;           //若任务无挂起则直接将任务就绪        OSRdyTbl[y]      |=  ptcb->OSTCBBitX;    }    OS_EventTaskRemove(ptcb, pevent);            //将调用的任务从事件等待列表中删除#if (OS_EVENT_MULTI_EN > 0u)  //删除所有与该任务相关的事件等待    if (ptcb->OSTCBEventMultiPtr != (OS_EVENT **)0) {   /* Remove this task from events' wait lists    */        OS_EventTaskRemoveMulti(ptcb, ptcb->OSTCBEventMultiPtr);        ptcb->OSTCBEventPtr       = (OS_EVENT  *)pevent;/* Return event as first multi-pend event ready*/    }#endif    return (prio);}
该函数是将调用任务从事件等待列表中删除。及置于就绪状态
void  OS_EventTaskRemove (OS_TCB   *ptcb,                          OS_EVENT *pevent){    INT8U  y;    y                       =  ptcb->OSTCBY;  //获取事件的组号    pevent->OSEventTbl[y]  &= (OS_PRIO)~ptcb->OSTCBBitX;    //清除相应位    if (pevent->OSEventTbl[y] == 0u) {        pevent->OSEventGrp &= (OS_PRIO)~ptcb->OSTCBBitY;    }}
该函数是将任务从事件控制块等待列表中去除。
好了,基本控制块结构就介绍到这里,接下来终于到了我们的正题。

首先 在基础单元的基础上,信号量并没有全部使用事件控制块的所有域,而只是使用了OSEventType、OSEventCnt、OSEventGrp和OSEventTbl域。对于OSEventPtr则保留,直接指向NULL,也就是(void *)0

信号量的操作一共包含6个函数

OS_EVENT  *OSSemCreate (INT16U cnt); //负责信号量的创建        void  OSSemPend (OS_EVENT  *pevent, INT32U  timeout, INT8U *perr); //信号量的挂起INT8U  OSSemPost (OS_EVENT *pevent);//信号量的释放OS_EVENT  *OSSemDel (OS_EVENT  *pevent, INT8U  opt,  INT8U  *perr);//信号量的删除INT8U  OSSemQuery (OS_EVENT     *pevent,  OS_SEM_DATA  *p_sem_data);//获取信号量的内部信息INT16U  OSSemAccept (OS_EVENT *pevent); //无等待申请信号量
其他函数2个
       INT8U  OSSemPendAbort (OS_EVENT  *pevent, INT8U      opt, INT8U     *perr);       void  OSSemSet (OS_EVENT  *pevent, INT16U     cnt,INT8U     *perr);
为了深入理解信号量,避免一些使用过程中可能出现的错误。我们来对信号量的操作函数源码进行一下简单的分析,这也就是开源的优点,可以清楚的看到内部的具体实现。
OS_EVENT  *OSSemCreate (INT16U cnt){    OS_EVENT  *pevent;#if OS_CRITICAL_METHOD == 3u                               /* Allocate storage for CPU status register */    OS_CPU_SR  cpu_sr = 0u;#endif#ifdef OS_SAFETY_CRITICAL_IEC61508    if (OSSafetyCriticalStartFlag == OS_TRUE) {        OS_SAFETY_CRITICAL_EXCEPTION();        return ((OS_EVENT *)0);    }#endif    if (OSIntNesting > 0u) {                               //中断中禁止创建信号量        return ((OS_EVENT *)0);                            /* ... can't CREATE from an ISR             */    }    OS_ENTER_CRITICAL();    pevent = OSEventFreeList;                              /* Get next free event control block        */    if (OSEventFreeList != (OS_EVENT *)0) {                /* See if pool of free ECB pool was empty   */        OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;    }    OS_EXIT_CRITICAL();    if (pevent != (OS_EVENT *)0) {  //成功获取事件控制块后表示信号量存在创建条件,于是开始信号量的参数初始化                pevent->OSEventType    = OS_EVENT_TYPE_SEM;        pevent->OSEventCnt     = cnt;                      /* Set semaphore value                      */        pevent->OSEventPtr     = (void *)0;                /* Unlink from ECB free list                */#if OS_EVENT_NAME_EN > 0u        pevent->OSEventName    = (INT8U *)(void *)"?";#endif        OS_EventWaitListInit(pevent);  //上面已经分析过了,见上面事件控制块部分    }    return (pevent); //最后返回成功创建的信号量地址}
该函数主要实现信号量的创建并初始化信号量的一些基本参数。cnt为共享资源的数目
void  OSSemPend (OS_EVENT  *pevent,                 INT32U     timeout,                 INT8U     *perr){#if OS_CRITICAL_METHOD == 3u                          /* Allocate storage for CPU status register      */    OS_CPU_SR  cpu_sr = 0u;#endif#ifdef OS_SAFETY_CRITICAL    if (perr == (INT8U *)0) { //主要检查perr参数地址是否合法        OS_SAFETY_CRITICAL_EXCEPTION();        return;    }#endif#if OS_ARG_CHK_EN > 0u    if (pevent == (OS_EVENT *)0) {                    /* Validate 'pevent'                             */        *perr = OS_ERR_PEVENT_NULL;        return;    }#endif    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {   /* Validate event block type                     */        *perr = OS_ERR_EVENT_TYPE;        return;    }    if (OSIntNesting > 0u) {                          /* See if called from ISR ...                    */        *perr = OS_ERR_PEND_ISR;                      /* ... can't PEND from an ISR                    */        return;    }    if (OSLockNesting > 0u) {                         /* See if called with scheduler locked ...       */        *perr = OS_ERR_PEND_LOCKED;                   /* ... can't PEND when locked                    */        return;    }    OS_ENTER_CRITICAL();    if (pevent->OSEventCnt > 0u) {     //存在共享资源 将直接占用一个资源并返回                       pevent->OSEventCnt--;                         /* ... decrement semaphore only if positive.     */        OS_EXIT_CRITICAL();        *perr = OS_ERR_NONE;        return;    }    //无资源,或资源被占用,需要等待                  /* Otherwise, must wait until event occurs       */    OSTCBCur->OSTCBStat     |= OS_STAT_SEM;           /* Resource not available, pend on semaphore     */    OSTCBCur->OSTCBStatPend  = OS_STAT_PEND_OK;    OSTCBCur->OSTCBDly       = timeout;               /* Store pend timeout in TCB                     */    OS_EventTaskWait(pevent);          //上面解释过,其主要是置位相应任务等待列表    OS_EXIT_CRITICAL();    OS_Sched();                   //开始任务调度    OS_ENTER_CRITICAL();    switch (OSTCBCur->OSTCBStatPend) {   //查询挂起状态(包括挂起完成和挂起超时、忽略挂起)                       case OS_STAT_PEND_OK:             *perr = OS_ERR_NONE;             break;        case OS_STAT_PEND_ABORT:             *perr = OS_ERR_PEND_ABORT;               /* Indicate that we aborted                      */             break;        case OS_STAT_PEND_TO:        default:             OS_EventTaskRemove(OSTCBCur, pevent);             *perr = OS_ERR_TIMEOUT;                  /* Indicate that we didn't get event 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;   //挂起完成,清除和等待事件表的联系                      */#if (OS_EVENT_MULTI_EN > 0u)    OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0; //清除与该TCB任务相关的所有事件标志#endif    OS_EXIT_CRITICAL();}
该函数的主要目的是申请一个信号量,如果信号量存在,将直接使用并返回。若不存在或者资源正在占用将挂起该任务,并等待信号量的释放。
INT16U  OSSemAccept (OS_EVENT *pevent){    INT16U     cnt;#if OS_CRITICAL_METHOD == 3u                          /* Allocate storage for CPU status register      */    OS_CPU_SR  cpu_sr = 0u;#endif#if OS_ARG_CHK_EN > 0u    if (pevent == (OS_EVENT *)0) {                    /* Validate 'pevent'                             */        return (0u);    }#endif    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {   /* Validate event block type                     */        return (0u);    }    OS_ENTER_CRITICAL();    cnt = pevent->OSEventCnt;    if (cnt > 0u) {                                   /* See if resource is available                  */        pevent->OSEventCnt--;                         /* Yes, decrement semaphore and notify caller    */    }    OS_EXIT_CRITICAL();    return (cnt);                                //返回当前剩余的信号量数目}
该函数比较简单,也是申请一个信号量,不过其与OSSemPend()的区别是,OSSemPend在资源被占用的时候会被挂起,而OSSemAccept()在遇到资源被占用,将直接返回,不存在等待挂起状态,其次,调用它将返回当前所有可使用的信号量

INT8U  OSSemPost (OS_EVENT *pevent){#if OS_CRITICAL_METHOD == 3u                          /* Allocate storage for CPU status register      */    OS_CPU_SR  cpu_sr = 0u;#endif#if OS_ARG_CHK_EN > 0u    if (pevent == (OS_EVENT *)0) {                    /* Validate 'pevent'                             */        return (OS_ERR_PEVENT_NULL);    }#endif    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {   /* Validate event block type                     */        return (OS_ERR_EVENT_TYPE);    }    OS_ENTER_CRITICAL();    if (pevent->OSEventGrp != 0u) {                   /* See if any task waiting for semaphore         */                                                      /* Ready HPT waiting on event                    */        (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK); //释放占用的信号量        OS_EXIT_CRITICAL();        OS_Sched();                                   /* Find HPT ready to run                         */        return (OS_ERR_NONE);    }    if (pevent->OSEventCnt < 65535u) {        //现存信号量++        pevent->OSEventCnt++;                         /* Increment semaphore count to register event   */        OS_EXIT_CRITICAL();        return (OS_ERR_NONE);    }    OS_EXIT_CRITICAL();                               /* Semaphore value has reached its maximum       */    return (OS_ERR_SEM_OVF);}

该函数主要是释放一个信号量资源,主要是清除该占用任务的事件标志组等待列表中的标志,及挂起态,同时信号量数目++;
OS_EVENT  *OSSemDel (OS_EVENT  *pevent,                     INT8U      opt,                     INT8U     *perr){    BOOLEAN    tasks_waiting;    OS_EVENT  *pevent_return;#if OS_CRITICAL_METHOD == 3u                               /* Allocate storage for CPU status register */    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) {                         /* Validate 'pevent'                        */        *perr = OS_ERR_PEVENT_NULL;        return (pevent);    }#endif    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {        /* Validate event block type                */        *perr = OS_ERR_EVENT_TYPE;        return (pevent);    }    if (OSIntNesting > 0u) {                               /* See if called from ISR ...               */        *perr = OS_ERR_DEL_ISR;                            /* ... can't DELETE from an ISR             */        return (pevent);    }    OS_ENTER_CRITICAL();    if (pevent->OSEventGrp != 0u) {                        /* See if any tasks waiting on semaphore    */        tasks_waiting = OS_TRUE;                          //判断是否存在等待该信号量的任务                                     */    } else {        tasks_waiting = OS_FALSE;                          /* No                                       */    }    switch (opt) {        case OS_DEL_NO_PEND:                               /* Delete semaphore only if no task waiting */             if (tasks_waiting == OS_FALSE) {#if OS_EVENT_NAME_EN > 0u                 pevent->OSEventName    = (INT8U *)(void *)"?";#endif                 pevent->OSEventType    = OS_EVENT_TYPE_UNUSED;                 pevent->OSEventPtr     = OSEventFreeList; /* Return Event Control Block to free list  */                 pevent->OSEventCnt     = 0u;                 OSEventFreeList        = pevent;          /* Get next free event control block        */                 OS_EXIT_CRITICAL();                 *perr                  = OS_ERR_NONE;                 pevent_return          = (OS_EVENT *)0;   /* Semaphore has been deleted               */             } else {                 OS_EXIT_CRITICAL();                 *perr                  = OS_ERR_TASK_WAITING; //否则信号量删除失败                 pevent_return          = pevent;             }             break;        case OS_DEL_ALWAYS:                                /* Always delete the semaphore              */             while (pevent->OSEventGrp != 0u) {            /* Ready ALL tasks waiting for semaphore    */                 (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_ABORT);             }//就绪所有等待任务#if OS_EVENT_NAME_EN > 0u             pevent->OSEventName    = (INT8U *)(void *)"?";#endif             pevent->OSEventType    = OS_EVENT_TYPE_UNUSED;             pevent->OSEventPtr     = OSEventFreeList;     /* Return Event Control Block to free list  */             pevent->OSEventCnt     = 0u;             OSEventFreeList        = pevent;              /* Get next free event control block        */             OS_EXIT_CRITICAL();             if (tasks_waiting == OS_TRUE) {               /* Reschedule only if task(s) were waiting  */                 OS_Sched();                               /* Find highest priority task ready to run  */             }//释放后启动任务调度             *perr                  = OS_ERR_NONE;             pevent_return          = (OS_EVENT *)0;       /* Semaphore has been deleted               */             break;        default:             OS_EXIT_CRITICAL();             *perr                  = OS_ERR_INVALID_OPT;             pevent_return          = pevent;             break;    }    return (pevent_return);}
该函数的目的是删除信号量。其存在两种情况:
1、信号量正在被占用,那么若选择删除无挂起任务,那么该次删除失败。不被占用将直接删除被将其链接到事件控制块链表。
2、信号量被占用,选择总是删除,那么首先将就绪所有等待任务,然后删除信号量往往不建议直接删除一个信号量,因为可能存在多个任务都在共享一个信号量,那么如果一个任务删除了该信号量,其他任务由于无法获取信号量而永久挂起,导致错误。
INT8U  OSSemQuery (OS_EVENT     *pevent,                   OS_SEM_DATA  *p_sem_data){    INT8U       i;    OS_PRIO    *psrc;    OS_PRIO    *pdest;#if OS_CRITICAL_METHOD == 3u                               /* Allocate storage for CPU status register */    OS_CPU_SR   cpu_sr = 0u;#endif#if OS_ARG_CHK_EN > 0u    if (pevent == (OS_EVENT *)0) {                         /* Validate 'pevent'                        */        return (OS_ERR_PEVENT_NULL);    }    if (p_sem_data == (OS_SEM_DATA *)0) {                  /* Validate 'p_sem_data'                    */        return (OS_ERR_PDATA_NULL);    }#endif    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {        /* Validate event block type                */        return (OS_ERR_EVENT_TYPE);    }    OS_ENTER_CRITICAL();    p_sem_data->OSEventGrp = pevent->OSEventGrp;           /* Copy message mailbox wait list           */    psrc                   = &pevent->OSEventTbl[0];    pdest                  = &p_sem_data->OSEventTbl[0];    for (i = 0u; i < OS_EVENT_TBL_SIZE; i++) { //拷贝信号量的信息        *pdest++ = *psrc++;    }    p_sem_data->OSCnt = pevent->OSEventCnt;                /* Get semaphore count                      */    OS_EXIT_CRITICAL();    return (OS_ERR_NONE);}
该函数比较简单,其目的也较为单一,就是将指定信号量的内容拷贝到OS_SEM_DATA的数据结构中,一般可以通过该函数来了解信号量的使用情况,如调试阶段实时打印信号量的使用和任务的情况

对于另外的两个函数

void  OSSemSet (OS_EVENT  *pevent,                INT16U     cnt,                INT8U     *perr){#if OS_CRITICAL_METHOD == 3u                          /* Allocate storage for CPU status register      */    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) {                    /* Validate 'pevent'                             */        *perr = OS_ERR_PEVENT_NULL;        return;    }#endif    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {   /* Validate event block type                     */        *perr = OS_ERR_EVENT_TYPE;        return;    }    OS_ENTER_CRITICAL();    *perr = OS_ERR_NONE;    if (pevent->OSEventCnt > 0u) {       //如果存在闲置的信号量,将直接设置        pevent->OSEventCnt = cnt;                     /* Yes, set it to the new value specified.       */    } else {   //若信号量为0,将必须判断是否存在等待该信号量的任务,如果存在,将不能设置,因为这将改变任务的运行状况,是危险的操作                                                                              */        if (pevent->OSEventGrp == 0u) {               /*      See if task(s) waiting?                  */            pevent->OSEventCnt = cnt;                 /*      No, OK to set the value                  */        } else {            *perr              = OS_ERR_TASK_WAITING;        }    }    OS_EXIT_CRITICAL();}
该函数是为了动态的修改信号量的数目而设置的。但是要注意,信号量紧缺(及为0)时,设置信号量将存在改变任务 运行状况的风险。
INT8U  OSSemPendAbort (OS_EVENT  *pevent,                       INT8U      opt,                       INT8U     *perr){    INT8U      nbr_tasks;#if OS_CRITICAL_METHOD == 3u                          /* Allocate storage for CPU status register      */    OS_CPU_SR  cpu_sr = 0u;#endif#ifdef OS_SAFETY_CRITICAL    if (perr == (INT8U *)0) {        OS_SAFETY_CRITICAL_EXCEPTION();        return (0u);    }#endif#if OS_ARG_CHK_EN > 0u    if (pevent == (OS_EVENT *)0) {                    /* Validate 'pevent'                             */        *perr = OS_ERR_PEVENT_NULL;        return (0u);    }#endif    if (pevent->OSEventType != OS_EVENT_TYPE_SEM) {   /* Validate event block type                     */        *perr = OS_ERR_EVENT_TYPE;        return (0u);    }    OS_ENTER_CRITICAL();    if (pevent->OSEventGrp != 0u) {                   /* See if any task waiting on semaphore?         */        nbr_tasks = 0u;        switch (opt) {            case OS_PEND_OPT_BROADCAST:   //忽略所有等待挂起任务                 while (pevent->OSEventGrp != 0u) {   /* Yes, ready ALL tasks waiting on semaphore     */                     (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_ABORT);                     nbr_tasks++;                 }                 break;            case OS_PEND_OPT_NONE://忽略最高优先级的任务            default:                                  /* No,  ready HPT       waiting on semaphore     */                 (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_ABORT);                 nbr_tasks++;                 break;        }        OS_EXIT_CRITICAL();        OS_Sched();                                   /* Find HPT ready to run                         */        *perr = OS_ERR_PEND_ABORT;        return (nbr_tasks); //返回忽略任务的数目    }    OS_EXIT_CRITICAL();    *perr = OS_ERR_NONE;    return (0u);                                      /* No tasks waiting on semaphore                 */}
该任务是选择性的忽略一些任务的挂起状态,可忽略所有任务或者仅仅将最高优先级任务置为就绪,
最后返回忽略任务的数目


以上就是所有关于信号量操作的函数,也是UCOSII中所有关于信号量的操作内容。但是我们往往有这样一种感觉,源码感觉看懂了,但是实际了操作吧又感觉似懂非懂,尤其是加入UCOS后,程序只要一出错,就懵逼了,哈哈哈,原谅我的直白,因为我也一样。
这是因为我们只是站在底层的角度去理解了他,但是真正的应用层面,我们并不熟悉。那么何为应用层面的理解呢?简单的讲,你了解信号量在一个实际应用中的运作原理吗?那么多任务共享一个信号量,那么他们之间怎么通信的,系统怎么知道哪一个任务应当被就绪呢?是什么让他们如此简单而又高效的联系起来。我们接下来就来分析分析。
任何对信号量的操作总结起来无非是对等待任务标志表和等待任务组的操作。当一个信号量开始创建,他的OSEventGrp和OSEventTbl域都为0,以表示无任务等待。当一个任务申请信号量,而此时信号量又紧缺时,他将在信号量等待任务标志表相应优先级位置置1,以表示该优先级的任务正在等待该信号量,然后任务挂起,及清除就绪标志,系统开展调度,PC指针离开该任务,此时存在两种情况可以使PC指针重新指向该任务。

1、任务挂起超时,由于运行时间减到0,此时在systick中断中,系统将直接设置任务就绪标志。        if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) {            ptcb->OSTCBStat  &= (INT8U)~(INT8U)OS_STAT_PEND_ANY;          /* Yes, Clear status flag   */            ptcb->OSTCBStatPend = OS_STAT_PEND_TO;                 /* Indicate PEND timeout    */        } else {            ptcb->OSTCBStatPend = OS_STAT_PEND_OK;        }        if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) {  /* Is task suspended?       */            OSRdyGrp               |= ptcb->OSTCBBitY;             /* No,  Make ready          */            OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;        }2、任务主动Post释放信号量,此时在Post的函数中将查找最高优先级任务并置为就绪。if (pevent->OSEventGrp != 0u) {                   /* See if any task waiting for semaphore         */                 */             (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK);             OS_EXIT_CRITICAL();             OS_Sched();                                   /* Find HPT ready to run                         */        return (OS_ERR_NONE);    }
    这样我们就可以实现信号量的挂起和释放了,是不是搞清楚了就很简单。
好了  信号量部分就写到这里。结束
时间:170701   19:23     吃饭去了,^_^






原创粉丝点击