μC/OS-III---I笔记5---多值信号量

多值信号量
  操作系统中利用信号量解决进程间的同步和互斥(互斥信号量)的问题,在多道程序环境下,操作系统如何实现进程之间的同步和互斥显得极为重要。比如对同一部分资源的访问是要互斥,不能在另一个进程A在访问的时候被其他的进程再访问这样两个进程相互影响就无法保证正常的访问操作。另一方面是任务间的同步,比如一个任务A依赖于另一个任务B的处理结果,在B处理完成时给A一个就绪信号让A开始使用结果进行相关处理。

  同样在使用之前要先定义一个信号量,然后调用创建OSSemCreate()函数创建信号量注意到函数内的等待队列初始化。每个信号量都有一个等待队列,当一个任务使用完公共资源部分或者完成了同步处理的一部分就会发布一个信号给等待的任务,等待任务会接到这个信号从而进行相应的操作。等待的任务由OS_PEND_LIST这个结构体变量管理TailPtr指向等待队列的最后一个Head指向第一个。

如图的数据结构:

 

1.创建多值信号量,这实际上是对多值信号量的一个初始化的部分这在USOC内多处都是这样叫的。

#if OS_CFG_SEM_EN > 0u
/*
************************************************************************************************************************
*                                                  CREATE A SEMAPHORE
*
* Description: This function creates a semaphore.
*
* Arguments  : p_sem         is a pointer to the semaphore to initialize.  Your application is responsible for
*                            allocating storage for the semaphore.
*
*              p_name        is a pointer to the name you would like to give the semaphore.
*
*              cnt           is the initial value for the semaphore.
*                            If used to share resources, you should initialize to the number of resources available.
*                            If used to signal the occurrence of event(s) then you should initialize to 0.
*
*              p_err         is a pointer to a variable that will contain an error code returned by this function.
*
*                                OS_ERR_NONE                    if the call was successful
*                                OS_ERR_CREATE_ISR              if you called this function from an ISR
*                                OS_ERR_ILLEGAL_CREATE_RUN_TIME if you are trying to create the semaphore after you
*                                                                 called OSSafetyCriticalStart().
*                                OS_ERR_NAME                    if 'p_name' is a NULL pointer
*                                OS_ERR_OBJ_CREATED             if the semaphore has already been created
*                                OS_ERR_OBJ_PTR_NULL            if 'p_sem'  is a NULL pointer
*                                OS_ERR_OBJ_TYPE                if 'p_sem' has already been initialized to a different
*                                                               object type
*
* Returns    : none
************************************************************************************************************************
*/

void  OSSemCreate (OS_SEM      *p_sem,
                   CPU_CHAR    *p_name,
                   OS_SEM_CTR   cnt,
                   OS_ERR      *p_err)
{
    CPU_SR_ALLOC();



#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

#ifdef OS_SAFETY_CRITICAL_IEC61508
    if (OSSafetyCriticalStartFlag == DEF_TRUE) {
       *p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
        return;
    }
#endif

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* Not allowed to be called from an ISR                   */
       *p_err = OS_ERR_CREATE_ISR;
        return;
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u
    if (p_sem == (OS_SEM *)0) {                             /* Validate 'p_sem'                                       */
       *p_err = OS_ERR_OBJ_PTR_NULL;
        return;
    }
#endif

    OS_CRITICAL_ENTER();
    //信号量变量初始化 
    p_sem->Type    = OS_OBJ_TYPE_SEM;                       /* Mark the data structure as a semaphore                 */
    p_sem->Ctr     = cnt;                                   /* Set semaphore value                                    */
    p_sem->TS      = (CPU_TS)0;
    p_sem->NamePtr = p_name;                                /* Save the name of the semaphore                         */
    //信号量上的等待队列初始化 
    OS_PendListInit(&p_sem->PendList);                      /* Initialize the waiting list                            */
#if OS_CFG_DBG_EN > 0u
    OS_SemDbgListAdd(p_sem);
#endif
    //信号量计数加1 
    OSSemQty++;
    OS_CRITICAL_EXIT_NO_SCHED();
   *p_err = OS_ERR_NONE;
}
OSSemCreate ()

2.创建完信号量,然后请求信号量,对于多值信号量先检查信号量是否可用,可用就直接返回信号量的可用数量,如果不可用选择阻塞任务,或者直接继续运行任务。

等待信号量流程:

 内部调用的函数的中文注释源码:

************************************************************************************************************************
*                                                  PEND ON SEMAPHORE
*
* Description: This function waits for a semaphore.
*
* Arguments  : p_sem         is a pointer to the semaphore
*
*              timeout       is an optional timeout period (in clock ticks).  If non-zero, your task will wait for the
*                            resource up to the amount of time (in 'ticks') specified by this argument.  If you specify
*                            0, however, your task will wait forever at the specified semaphore or, until the resource
*                            becomes available (or the event occurs).
*
*              opt           determines whether the user wants to block if the semaphore is not available or not:
*
*                                OS_OPT_PEND_BLOCKING
*                                OS_OPT_PEND_NON_BLOCKING
*
*              p_ts          is a pointer to a variable that will receive the timestamp of when the semaphore was posted
*                            or pend aborted or the semaphore deleted.  If you pass a NULL pointer (i.e. (CPU_TS*)0)
*                            then you will not get the timestamp.  In other words, passing a NULL pointer is valid
*                            and indicates that you don't need the timestamp.
*
*              p_err         is a pointer to a variable that will contain an error code returned by this function.
*
*                                OS_ERR_NONE               The call was successful and your task owns the resource
*                                                          or, the event you are waiting for occurred.
*                                OS_ERR_OBJ_DEL            If 'p_sem' was deleted
*                                OS_ERR_OBJ_PTR_NULL       If 'p_sem' is a NULL pointer.
*                                OS_ERR_OBJ_TYPE           If 'p_sem' is not pointing at a semaphore
*                                OS_ERR_OPT_INVALID        If you specified an invalid value for 'opt'
*                                OS_ERR_PEND_ABORT         If the pend was aborted by another task
*                                OS_ERR_PEND_ISR           If you called this function from an ISR and the result
*                                                          would lead to a suspension.
*                                OS_ERR_PEND_WOULD_BLOCK   If you specified non-blocking but the semaphore was not
*                                                          available.
*                                OS_ERR_SCHED_LOCKED       If you called this function when the scheduler is locked
*                                OS_ERR_STATUS_INVALID     Pend status is invalid
*                                OS_ERR_TIMEOUT            The semaphore was not received within the specified
*                                                          timeout.
*
*
* Returns    : The current value of the semaphore counter or 0 if not available.
************************************************************************************************************************
*/

OS_SEM_CTR  OSSemPend (OS_SEM   *p_sem,
                       OS_TICK   timeout,
                       OS_OPT    opt,
                       CPU_TS   *p_ts,
                       OS_ERR   *p_err)
{
    OS_SEM_CTR    ctr;
    OS_PEND_DATA  pend_data;
    CPU_SR_ALLOC();



#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((OS_SEM_CTR)0);
    }
#endif

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* Not allowed to call from an ISR                        */
       *p_err = OS_ERR_PEND_ISR;
        return ((OS_SEM_CTR)0);
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u
    if (p_sem == (OS_SEM *)0) {                             /* Validate 'p_sem'                                       */
       *p_err = OS_ERR_OBJ_PTR_NULL;
        return ((OS_SEM_CTR)0);
    }
    switch (opt) {                                          /* Validate 'opt'                                         */ 
        case OS_OPT_PEND_BLOCKING:
        case OS_OPT_PEND_NON_BLOCKING:
             break;

        default:
            *p_err = OS_ERR_OPT_INVALID;
             return ((OS_SEM_CTR)0);
    }
#endif

#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
    if (p_sem->Type != OS_OBJ_TYPE_SEM) {                   /* Make sure semaphore was created                        */
       *p_err = OS_ERR_OBJ_TYPE;
        return ((OS_SEM_CTR)0);
    }
#endif

    if (p_ts != (CPU_TS *)0) {
       *p_ts  = (CPU_TS)0;                                  /* Initialize the returned timestamp                      */
    }
    CPU_CRITICAL_ENTER();
    //资源有效直接返回可用的数量 
    if (p_sem->Ctr > (OS_SEM_CTR)0) {                       /* Resource available?                                    */
        p_sem->Ctr--;                                       /* Yes, caller may proceed                                */
        if (p_ts != (CPU_TS *)0) {
           *p_ts  = p_sem->TS;                              /*      get timestamp of last post                        */
        }
        ctr   = p_sem->Ctr;
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_NONE;
        return (ctr);
    }
    //是否阻塞任务 
    if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) {    /* Caller wants to block if not available?                */
        ctr   = p_sem->Ctr;                                 /* No                                                     */
        CPU_CRITICAL_EXIT();
       *p_err = OS_ERR_PEND_WOULD_BLOCK;
        return (ctr);
    } else {                                                /* Yes                                                    */
        if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {    /* Can't pend when the scheduler is locked                */
            CPU_CRITICAL_EXIT();
           *p_err = OS_ERR_SCHED_LOCKED;
            return ((OS_SEM_CTR)0);
        }
    }
                                                                /* Lock the scheduler/re-enable interrupts                */
    OS_CRITICAL_ENTER_CPU_EXIT();
    //阻塞任务进行等待 
    OS_Pend(&pend_data,                                     /* Block task pending on Semaphore                        */
            (OS_PEND_OBJ *)((void *)p_sem),
            OS_TASK_PEND_ON_SEM,
            timeout);

    OS_CRITICAL_EXIT_NO_SCHED();
    //挂起当前任务执行其他任务 
    OSSched();                                              /* Find the next highest priority task ready to run       */
    //当前任务重新就绪检查返回等待结果 
    CPU_CRITICAL_ENTER();
    switch (OSTCBCurPtr->PendStatus) {
        case OS_STATUS_PEND_OK:                             /* We got the semaphore                                   */
             if (p_ts != (CPU_TS *)0) {
                *p_ts  =  OSTCBCurPtr->TS;
             }
            *p_err = OS_ERR_NONE;
             break;

        case OS_STATUS_PEND_ABORT:                          /* Indicate that we aborted                               */
             if (p_ts != (CPU_TS *)0) {
                *p_ts  =  OSTCBCurPtr->TS;
             }
            *p_err = OS_ERR_PEND_ABORT;
             break;

        case OS_STATUS_PEND_TIMEOUT:                        /* Indicate that we didn't get semaphore within timeout   */
             if (p_ts != (CPU_TS *)0) {
                *p_ts  = (CPU_TS  )0;
             }
            *p_err = OS_ERR_TIMEOUT;
             break;

        case OS_STATUS_PEND_DEL:                            /* Indicate that object pended on has been deleted        */
             if (p_ts != (CPU_TS *)0) {
                *p_ts  =  OSTCBCurPtr->TS;
             }
            *p_err = OS_ERR_OBJ_DEL;
             break;

        default:
            *p_err = OS_ERR_STATUS_INVALID;
             CPU_CRITICAL_EXIT();
             return ((OS_SEM_CTR)0);
    }
    ctr = p_sem->Ctr;
    CPU_CRITICAL_EXIT();
    return (ctr);
}
OSSemPend ()
************************************************************************************************************************
*                                             BLOCK A TASK PENDING ON EVENT
*
* Description: This function is called to place a task in the blocked state waiting for an event to occur. This function
*              exist because it is common to a number of OSxxxPend() services.
*
* Arguments  : p_pend_data    is a pointer to an object used to link the task being blocked to the list of task(s)
*              -----------    pending on the desired object.

*              p_obj          is a pointer to the object to pend on.  If there are no object used to pend on then
*              -----          the caller must pass a NULL pointer.
*
*              pending_on     Specifies what the task will be pending on:
*
*                                 OS_TASK_PEND_ON_FLAG
*                                 OS_TASK_PEND_ON_TASK_Q     <- No object (pending for a message sent to the task)
*                                 OS_TASK_PEND_ON_MUTEX
*                                 OS_TASK_PEND_ON_Q
*                                 OS_TASK_PEND_ON_SEM
*                                 OS_TASK_PEND_ON_TASK_SEM   <- No object (pending on a signal sent to the task)
*
*              timeout        Is the amount of time the task will wait for the event to occur.
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_Pend (OS_PEND_DATA  *p_pend_data,
               OS_PEND_OBJ   *p_obj,
               OS_STATE       pending_on,
               OS_TICK        timeout)
{
    OS_PEND_LIST  *p_pend_list;



    OSTCBCurPtr->PendOn     = pending_on;                    /* Resource not available, wait until it is              */
    OSTCBCurPtr->PendStatus = OS_STATUS_PEND_OK;
    //×èÈûÈÎÎñ 
    OS_TaskBlock(OSTCBCurPtr,                                /* Block the task and add it to the tick list if needed  */
                 timeout);

    if (p_obj != (OS_PEND_OBJ *)0) {                         /* Add the current task to the pend list ...             */
        //È¡³öÐźÅÁ¿Ïµĵȴýlist 
        p_pend_list             = &p_obj->PendList;          /* ... if there is an object to pend on                  */
        //ÉèÖÃdataµÄµÈ´ý¶ÔÏó 
        p_pend_data->PendObjPtr = p_obj;                     /* Save the pointer to the object pending on             */
        //p_pend_data³õʼ»¯ 
        OS_PendDataInit((OS_TCB       *)OSTCBCurPtr,         /* Initialize the remaining field                        */
                        (OS_PEND_DATA *)p_pend_data,
                        (OS_OBJ_QTY    )1);
        //°´ÈÎÎñÓÅÏȼ¶ÅÅÐò½«µÈ´ýÊý¾Ý²åÈëµÈ´ý¶ÓÁÐ  
        OS_PendListInsertPrio(p_pend_list,                   /* Insert in the pend list in priority order             */
                              p_pend_data);
    } else {
        OSTCBCurPtr->PendDataTblEntries = (OS_OBJ_QTY    )0; /* If no object being pended on the clear these fields   */
        OSTCBCurPtr->PendDataTblPtr     = (OS_PEND_DATA *)0; /* ... in the TCB                                        */
    }
#if OS_CFG_DBG_EN > 0u
    OS_PendDbgNameAdd(p_obj,
                      OSTCBCurPtr);
#endif
}
OS_Pend ()
************************************************************************************************************************
*                                                     BLOCK A TASK
*
* Description: This function is called to remove a task from the ready list and also insert it in the timer tick list if
*              the specified timeout is non-zero.
*
* Arguments  : p_tcb          is a pointer to the OS_TCB of the task block
*              -----
*
*              timeout        is the desired timeout
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_TaskBlock (OS_TCB   *p_tcb,
                    OS_TICK   timeout)
{
    OS_ERR  err;

    //检查超时时间为0? 
    if (timeout > (OS_TICK)0) {                             /* Add task to tick list if timeout non zero               */
        //按超时时间设置延时阻塞,具体见时间管理 
        OS_TickListInsert(p_tcb,
                          timeout,
                          OS_OPT_TIME_TIMEOUT,
                         &err);
        //检查错误码 
        if (err == OS_ERR_NONE) {
            p_tcb->TaskState = OS_TASK_STATE_PEND_TIMEOUT;
        } else {
            p_tcb->TaskState = OS_TASK_STATE_PEND;
        }
    } else {
        //是  永久等待 
        p_tcb->TaskState = OS_TASK_STATE_PEND;
    }
    OS_RdyListRemove(p_tcb);
}
OS_TaskBlock()
************************************************************************************************************************
*                                              INITIALIZE A WAIT LIST TABLE
*
* Description: This function is called to initialize the fields of a table of OS_PEND_DATA entries.  It's assumed that
*              the .PendObjPtr field of each entry in the table is set by the caller and thus will NOT be touched by
*              this function.
*
* Arguments  : p_tcb              is a pointer to the TCB of the task that we want to pend abort.
*              -----
*
*              p_pend_data_tbl    is a pointer to a table (see below) of OS_PEND_DATA elements to initialize.
*              ---------------
*
*                                  .PendObjPtr .RdyObjPtr .RdyMsgPtr .RdyMsgSize .RdyTS .TCBPtr .NextPtr .PrevPtr
*                                 +-----------+----------+----------+-----------+------+-------+--------+--------+    ^
*               p_pend_data_tbl-> |     ?     |  0       | 0        | 0         | 0    | p_tcb | 0      | 0      |    |
*                                 +-----------+----------+----------+-----------+------+-------+--------+--------+    |
*                                 |     ?     |  0       | 0        | 0         | 0    | p_tcb | 0      | 0      |    |
*                                 +-----------+----------+----------+-----------+------+-------+--------+--------+    |
*                                 |     ?     |  0       | 0        | 0         | 0    | p_tcb | 0      | 0      |    |
*                                 +-----------+----------+----------+-----------+------+-------+--------+--------+  size
*                                 |     ?     |  0       | 0        | 0         | 0    | p_tcb | 0      | 0      |    |
*                                 +-----------+----------+----------+-----------+------+-------+--------+--------+    |
*                                 |     ?     |  0       | 0        | 0         | 0    | p_tcb | 0      | 0      |    |
*                                 +-----------+----------+----------+-----------+------+-------+--------+--------+    |
*                                 |     ?     |  0       | 0        | 0         | 0    | p_tcb | 0      | 0      |    |
*                                 +-----------+----------+----------+-----------+------+-------+--------+--------+    V
*
*              tbl_size           is the size of the table in number of entries
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application must not call it.
*
*              2) It's possible for the table to be of size 1 when multi-pend is not used
*
*              3) Note that the .PendObjPtr is NOT touched because it's assumed to be set by the caller.
************************************************************************************************************************
*/

void  OS_PendDataInit (OS_TCB        *p_tcb,
                       OS_PEND_DATA  *p_pend_data_tbl,
                       OS_OBJ_QTY     tbl_size)
{
    OS_OBJ_QTY  i;


    //将任务控制块对应的指针参数初始化 
    p_tcb->PendDataTblEntries = tbl_size;                   /* Link the TCB to the beginning of the table             */
    p_tcb->PendDataTblPtr     = p_pend_data_tbl;
    //这里一般调用都是执行一次 
    for (i = 0u; i < tbl_size; i++) {
        p_pend_data_tbl->NextPtr    = (OS_PEND_DATA *)0;    /* Initialize all the fields                              */
        p_pend_data_tbl->PrevPtr    = (OS_PEND_DATA *)0;
        p_pend_data_tbl->RdyObjPtr  = (OS_PEND_OBJ  *)0;
        p_pend_data_tbl->RdyMsgPtr  = (void         *)0;
        p_pend_data_tbl->RdyMsgSize = (OS_MSG_SIZE   )0;
        p_pend_data_tbl->RdyTS      = (CPU_TS        )0;
        p_pend_data_tbl->TCBPtr     = p_tcb;                /* Every entry points back to the TCB of the task         */
        p_pend_data_tbl++;
    }
}
OS_PendDataInit()
************************************************************************************************************************
*                                   INSERT PEND DATA BASED ON IT'S PRIORITY IN A LIST
*
* Description: This function is called to place an OS_PEND_DATA entry in a linked list based on its priority.  The
*              highest priority being placed at the head of the list.  It's assumed that the OS_PEND_DATA entry to
*              insert points to the TCB of the task being inserted.  The TCB is also assumed to contain the priority
*              of the task in its .Prio field.
*
*              CASE 0: Insert in an empty list.
*
*                     OS_PEND_LIST
*                     +---------------+
*                     | TailPtr       |-> 0
*                     +---------------+
*                     | HeadPtr       |-> 0
*                     +---------------+
*                     | NbrEntries=0  |
*                     +---------------+
*
*
*
*              CASE 1: Insert BEFORE or AFTER an OS_TCB
*
*                     OS_PEND_LIST
*                     +--------------+         OS_PEND_DATA
*                     | TailPtr      |--+---> +------------+
*                     +--------------+  |     | NextPtr    |->0
*                     | HeadPtr      |--/     +------------+
*                     +--------------+     0<-| PrevPtr    |
*                     | NbrEntries=1 |        +------------+
*                     +--------------+        |            |
*                                             +------------+
*                                             |            |
*                                             +------------+
*
*
*                     OS_PEND_LIST
*                     +--------------+
*                     | TailPtr      |-----------------------------------------------+
*                     +--------------+         OS_PEND_DATA         OS_PEND_DATA     |    OS_PEND_DATA
*                     | HeadPtr      |------> +------------+       +------------+    +-> +------------+
*                     +--------------+        | NextPtr    |------>| NextPtr    | ...... | NextPtr    |->0
*                     | NbrEntries=N |        +------------+       +------------+        +------------+
*                     +--------------+     0<-| PrevPtr    |<------| PrevPtr    | ...... | PrevPtr    |
*                                             +------------+       +------------+        +------------+
*                                             |            |       |            |        |            |
*                                             +------------+       +------------+        +------------+
*                                             |            |       |            |        |            |
*                                             +------------+       +------------+        +------------+
*
*
* Arguments  : p_pend_list    is a pointer to the OS_PEND_LIST where the OS_PEND_DATA entry will be inserted
*              -----------
*
*              p_pend_data    is the OS_PEND_DATA to insert in the list
*              -----------
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
*
*              2) 'p_pend_data->TCBPtr->Prio' contains the priority of the TCB associated with the entry to insert.
*                 We can compare this priority with the priority of other entries in the list.
************************************************************************************************************************
*/

void  OS_PendListInsertPrio (OS_PEND_LIST  *p_pend_list,
                             OS_PEND_DATA  *p_pend_data)
{
    OS_PRIO        prio;
    OS_TCB        *p_tcb;
    OS_TCB        *p_tcb_next;
    OS_PEND_DATA  *p_pend_data_prev;
    OS_PEND_DATA  *p_pend_data_next;



    p_tcb = p_pend_data->TCBPtr;                                      /* Obtain the priority of the task to insert    */
    prio  = p_tcb->Prio;
    if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0) {                   /* CASE 0: Insert when there are no entries     */
        p_pend_list->NbrEntries = (OS_OBJ_QTY)1;                      /*         This is the first entry              */
        p_pend_data->NextPtr    = (OS_PEND_DATA *)0;                  /*         No other OS_PEND_DATAs in the list   */
        p_pend_data->PrevPtr    = (OS_PEND_DATA *)0;
        p_pend_list->HeadPtr    = p_pend_data;                        /*                                              */
        p_pend_list->TailPtr    = p_pend_data;
    } else {
        p_pend_list->NbrEntries++;                                    /* CASE 1: One more OS_PEND_DATA in the list    */
        p_pend_data_next = p_pend_list->HeadPtr;
        while (p_pend_data_next != (OS_PEND_DATA *)0) {               /*         Find the position where to insert    */
            p_tcb_next   = p_pend_data_next->TCBPtr;
            if (prio < p_tcb_next->Prio) {
                break;                                                /*         Found! ... insert BEFORE current     */
            } else {
                p_pend_data_next = p_pend_data_next->NextPtr;         /*         Not Found, follow the list           */
            }
        }
        //开始插入
         
        //末尾 
        if (p_pend_data_next == (OS_PEND_DATA *)0) {                  /*         TCB to insert is lower in prio       */
            p_pend_data->NextPtr      = (OS_PEND_DATA *)0;            /*         ... insert at the tail.              */
            p_pend_data_prev          = p_pend_list->TailPtr;
            p_pend_data->PrevPtr      = p_pend_data_prev;
            p_pend_data_prev->NextPtr = p_pend_data;
            p_pend_list->TailPtr      = p_pend_data;
        } else {
            //开头 
            if (p_pend_data_next->PrevPtr == (OS_PEND_DATA *)0) {     /*         Is new TCB highest priority?         */
                p_pend_data_next->PrevPtr  = p_pend_data;             /*         Yes, insert as new Head of list      */
                p_pend_data->PrevPtr       = (OS_PEND_DATA *)0;
                p_pend_data->NextPtr       = p_pend_data_next;
                p_pend_list->HeadPtr       = p_pend_data;
            } else {
                //中间 
                p_pend_data_prev           = p_pend_data_next->PrevPtr;/*        No,  insert in between two entries   */
                p_pend_data->PrevPtr       = p_pend_data_prev;
                p_pend_data->NextPtr       = p_pend_data_next;
                p_pend_data_prev->NextPtr  = p_pend_data;
                p_pend_data_next->PrevPtr  = p_pend_data;
            }
        }
    }
}
OS_PendListInsertPrio()

3.发布信号量,分发布给信号量等待队列上所有等待任务还是发布全部等待任务中的最高优先级的任务,然后调用OS_Post()进行单个任务的发布而对于每个任务还要判断其是否等待多个内核对象,如果是则要仅仅发布任务的一个内核对象不能将任务脱离等待列表加入就绪任务列表,如果任务仅仅等待这一个对象就可将任务脱离等待列表并加入就绪列表下次任务调度时就会执行。

 先关函数的中文注释:

************************************************************************************************************************
*                                                 POST TO A SEMAPHORE
*
* Description: This function signals a semaphore
*
* Arguments  : p_sem    is a pointer to the semaphore
*
*              opt      determines the type of POST performed:
*
*                           OS_OPT_POST_1            POST and ready only the highest priority task waiting on semaphore
*                                                    (if tasks are waiting).
*                           OS_OPT_POST_ALL          POST to ALL tasks that are waiting on the semaphore
*
*                           OS_OPT_POST_NO_SCHED     Do not call the scheduler
*
*                           Note(s): 1) OS_OPT_POST_NO_SCHED can be added with one of the other options.
*
*              p_err    is a pointer to a variable that will contain an error code returned by this function.
*
*                           OS_ERR_NONE          The call was successful and the semaphore was signaled.
*                           OS_ERR_OBJ_PTR_NULL  If 'p_sem' is a NULL pointer.
*                           OS_ERR_OBJ_TYPE      If 'p_sem' is not pointing at a semaphore
*                           OS_ERR_SEM_OVF       If the post would cause the semaphore count to overflow.
*
* Returns    : The current value of the semaphore counter or 0 upon error.
************************************************************************************************************************
*/

OS_SEM_CTR  OSSemPost (OS_SEM  *p_sem,
                       OS_OPT   opt,
                       OS_ERR  *p_err)
{
    OS_SEM_CTR  ctr;
    CPU_TS      ts;



#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((OS_SEM_CTR)0);
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u
    if (p_sem == (OS_SEM *)0) {                             /* Validate 'p_sem'                                       */
       *p_err  = OS_ERR_OBJ_PTR_NULL;
        return ((OS_SEM_CTR)0);
    }
    switch (opt) {                                          /* Validate 'opt'                                         */
        case OS_OPT_POST_1:
        case OS_OPT_POST_ALL:
        case OS_OPT_POST_1   | OS_OPT_POST_NO_SCHED:
        case OS_OPT_POST_ALL | OS_OPT_POST_NO_SCHED:
             break;

        default:
            *p_err =  OS_ERR_OPT_INVALID;
             return ((OS_SEM_CTR)0u);
    }
#endif

#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
    if (p_sem->Type != OS_OBJ_TYPE_SEM) {                   /* Make sure semaphore was created                        */
       *p_err = OS_ERR_OBJ_TYPE;
        return ((OS_SEM_CTR)0);
    }
#endif

    ts = OS_TS_GET();                                       /* Get timestamp                                          */
    //延迟发布 
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* See if called from an ISR                              */
        OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_SEM,           /* Post to ISR queue                                      */
                    (void      *)p_sem,
                    (void      *)0,
                    (OS_MSG_SIZE)0,
                    (OS_FLAGS   )0,
                    (OS_OPT     )opt,
                    (CPU_TS     )ts,
                    (OS_ERR    *)p_err);
        return ((OS_SEM_CTR)0);
    }
#endif
    //正常发布 
    ctr = OS_SemPost(p_sem,                                 /* Post to semaphore                                      */
                     opt,
                     ts,
                     p_err);

    return (ctr);
}
OSSemPost()
************************************************************************************************************************
*                                                 POST TO A SEMAPHORE
*
* Description: This function signals a semaphore
*
* Arguments  : p_sem    is a pointer to the semaphore
*
*              opt      determines the type of POST performed:
*
*                           OS_OPT_POST_1            POST to a single waiting task
*                           OS_OPT_POST_ALL          POST to ALL tasks that are waiting on the semaphore
*
*                           OS_OPT_POST_NO_SCHED     Do not call the scheduler
*
*                           Note(s): 1) OS_OPT_POST_NO_SCHED can be added with one of the other options.
*
*              ts       is a timestamp indicating when the post occurred.
*
*              p_err    is a pointer to a variable that will contain an error code returned by this function.
*
*                           OS_ERR_NONE          The call was successful and the semaphore was signaled.
*                           OS_ERR_OBJ_PTR_NULL  If 'p_sem' is a NULL pointer.
*                           OS_ERR_OBJ_TYPE      If 'p_sem' is not pointing at a semaphore
*                           OS_ERR_SEM_OVF       If the post would cause the semaphore count to overflow.
*
* Returns    : The current value of the semaphore counter or 0 upon error.
*
* Note(s)    : This function is INTERNAL to uC/OS-III and your application should not call it.
************************************************************************************************************************
*/

OS_SEM_CTR  OS_SemPost (OS_SEM  *p_sem,
                        OS_OPT   opt,
                        CPU_TS   ts,
                        OS_ERR  *p_err)
{
    OS_OBJ_QTY     cnt;
    OS_SEM_CTR     ctr;
    OS_PEND_LIST  *p_pend_list;
    OS_PEND_DATA  *p_pend_data;
    OS_PEND_DATA  *p_pend_data_next;
    OS_TCB        *p_tcb;
    CPU_SR_ALLOC();



    CPU_CRITICAL_ENTER();
    //取出信号量内的等待队列 
    p_pend_list = &p_sem->PendList;
    //没有任何任务等待此信号量 
    if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0) {         /* Any task waiting on semaphore?                         */
        //变量范围检查 
        switch (sizeof(OS_SEM_CTR)) {
            case 1u:
                 if (p_sem->Ctr == DEF_INT_08U_MAX_VAL) {
                     CPU_CRITICAL_EXIT();
                    *p_err = OS_ERR_SEM_OVF;
                     return ((OS_SEM_CTR)0);
                 }
                 break;

            case 2u:
                 if (p_sem->Ctr == DEF_INT_16U_MAX_VAL) {
                     CPU_CRITICAL_EXIT();
                    *p_err = OS_ERR_SEM_OVF;
                     return ((OS_SEM_CTR)0);
                 }
                 break;

            case 4u:
                 if (p_sem->Ctr == DEF_INT_32U_MAX_VAL) {
                     CPU_CRITICAL_EXIT();
                    *p_err = OS_ERR_SEM_OVF;
                     return ((OS_SEM_CTR)0);
                 }
                 break;

            default:
                 break;
        }
        //信号量可用加1 
        p_sem->Ctr++;                                       /* No                                                     */
        ctr       = p_sem->Ctr;
        p_sem->TS = ts;                                     /* Save timestamp in semaphore control block              */
        CPU_CRITICAL_EXIT();
       *p_err     = OS_ERR_NONE;
        return (ctr);
    }

    OS_CRITICAL_ENTER_CPU_EXIT();
    //发布选项给等待队列上的所有任务还是一个?  
    if ((opt & OS_OPT_POST_ALL) != (OS_OPT)0) {             /* Post message to all tasks waiting?                     */
        //全部
        cnt = p_pend_list->NbrEntries;                      /* Yes                                                    */
    } else {
        //优先级最高的一个 
        cnt = (OS_OBJ_QTY)1;                                /* No                                                     */
    }
    //取出list头 
    p_pend_data = p_pend_list->HeadPtr;
    //循环从头开始操作任务 
    while (cnt > 0u) {
        p_tcb            = p_pend_data->TCBPtr;
        p_pend_data_next = p_pend_data->NextPtr;
        //发布给等待任务 
        OS_Post((OS_PEND_OBJ *)((void *)p_sem),
                p_tcb,
                (void      *)0,
                (OS_MSG_SIZE)0,
                ts);
        p_pend_data = p_pend_data_next;
        cnt--;
    }
    ctr = p_sem->Ctr;
    OS_CRITICAL_EXIT_NO_SCHED();
    //发布后启动调度? 
    if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) { 
    //
        OSSched();                                          /* Run the scheduler                                      */
    }
   *p_err = OS_ERR_NONE;
    return (ctr);
}

#endif
OS_SemPost ()
************************************************************************************************************************
*                                                   POST TO A TASK
*
* Description: This function is called to post to a task.  This function exist because it is common to a number of
*              OSxxxPost() services.
*
* Arguments  : p_obj          Is a pointer to the object being posted to or NULL pointer if there is no object
*              -----
*
*              p_tcb          Is a pointer to the OS_TCB that will receive the 'post'
*              -----
*
*              p_void         If we are posting a message to a task, this is the message that the task will receive
*
*              msg_size       If we are posting a message to a task, this is the size of the message
*
*              ts             The timestamp as to when the post occurred
*
* Returns    : none
*
* Note(s)    : This function is INTERNAL to uC/OS-III and your application should not call it.
************************************************************************************************************************
*/

void  OS_Post (OS_PEND_OBJ  *p_obj,
               OS_TCB       *p_tcb,
               void         *p_void,
               OS_MSG_SIZE   msg_size,
               CPU_TS        ts)
{
    //检查任务状态 
    switch (p_tcb->TaskState) {
        //非该有状态 
        case OS_TASK_STATE_RDY:                                  /* Cannot Pend Abort a task that is ready            */
        case OS_TASK_STATE_DLY:                                  /* Cannot Pend Abort a task that is delayed          */
        case OS_TASK_STATE_SUSPENDED:                            /* Cannot Post a suspended task                      */
        case OS_TASK_STATE_DLY_SUSPENDED:                        /* Cannot Post a suspended task that was also dly'd  */
             break;
        //任务同时仅仅是等待信号量状态的 
        case OS_TASK_STATE_PEND:
        case OS_TASK_STATE_PEND_TIMEOUT:
            //任务等待多个内核对象? 
             if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {
                 //是 发布一个内核对象给任务控制块 
                 OS_Post1(p_obj,                                 /* Indicate which object was posted to               */
                          p_tcb,
                          p_void,
                          msg_size,
                          ts);
             } else {
                 //否 发布一个内核对象给任务控制块
#if (OS_MSG_EN > 0u)
                 p_tcb->MsgPtr  = p_void;                        /* Deposit message in OS_TCB of task waiting         */
                 p_tcb->MsgSize = msg_size;                      /* ... assuming posting a message                    */
#endif
                 p_tcb->TS      = ts;
             }
             if (p_obj != (OS_PEND_OBJ *)0) {
                 //将任务从任务从等待list移除(这里是一个双向链表额删除操作) 
                 OS_PendListRemove(p_tcb);                       /* Remove task from wait list(s)                     */
#if OS_CFG_DBG_EN > 0u
                 OS_PendDbgNameRemove(p_obj,
                                      p_tcb);
#endif
             }
             //将任务加入就绪list下次任务调度就会被执行 
             OS_TaskRdy(p_tcb);                                  /* Make task ready to run                            */
             //修改任务状态标记值 
             p_tcb->TaskState  = OS_TASK_STATE_RDY;
             p_tcb->PendStatus = OS_STATUS_PEND_OK;              /* Clear pend status                                 */
             p_tcb->PendOn     = OS_TASK_PEND_ON_NOTHING;        /* Indicate no longer pending                        */
             break;
        //任务同时是等待挂起状态的设置为挂起状态 
        case OS_TASK_STATE_PEND_SUSPENDED:
        case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
             if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {
                 //这里和等待一个内核对象不同的是没有将任务加入到就绪list 
                 OS_Post1(p_obj,                                 /* Indicate which object was posted to               */
                          p_tcb,
                          p_void,
                          msg_size,
                          ts);
             } else {
#if (OS_MSG_EN > 0u)
                 p_tcb->MsgPtr  = p_void;                        /* Deposit message in OS_TCB of task waiting         */
                 p_tcb->MsgSize = msg_size;                      /* ... assuming posting a message                    */
#endif
                 p_tcb->TS      = ts;
             }
             //将任务从任务从等待list移除(这里是一个双向链表额删除操作)
             OS_TickListRemove(p_tcb);                           /* Cancel any timeout                                */
             if (p_obj != (OS_PEND_OBJ *)0) {
                 OS_PendListRemove(p_tcb);                       /* Remove task from wait list(s)                     */
#if OS_CFG_DBG_EN > 0u
                 OS_PendDbgNameRemove(p_obj,
                                      p_tcb);
#endif
             }
             //注意这里和上面的不同 
             p_tcb->TaskState  = OS_TASK_STATE_SUSPENDED;
             p_tcb->PendStatus = OS_STATUS_PEND_OK;              /* Clear pend status                                 */
             p_tcb->PendOn     = OS_TASK_PEND_ON_NOTHING;        /* Indicate no longer pending                        */
             break;

        default:
             break;
    }
}
OS_Post ()
************************************************************************************************************************
*                                           POST TO A TASK PENDING ON MULTIPLE OBJECTS
*
* Description: This function is called when a task is pending on multiple objects and the object has been posted to.
*              This function needs to indicate to the caller which object was posted to by placing the address of the
*              object in the OS_PEND_DATA table corresponding to the posted object.
*
*              For example, if the task pends on six (6) objects, the address of those 6 objects are placed in the
*              .PendObjPtr field of the OS_PEND_DATA table as shown below.  Note that the .PendDataTblEntries would be
*              set to six (6) in this case.  As shown, when the pend call returns because a task or an ISR posted to
*              'Obj C' then, only the one entry contains the filled in data and the other entries contains NULL pointers
*              and zero data.
*
*              You should note that the NULL pointers are zero data values are actually filled in by the pend call.
*
*
*                                           .PendObjPtr    .RdyObjPtr     .RdyMsgPtr     .RdyMsgSize    .RdyTS
*                                         +--------------+--------------+--------------+--------------+--------------+
*              p_tcb->PendDataTblPtr  ->  |  Obj A       |  0           | 0            | 0            | 0            |
*                                         +--------------+--------------+--------------+--------------+--------------+
*                                         |  Obj B       |  0           | 0            | 0            | 0            |
*                                         +--------------+--------------+--------------+--------------+--------------+
*                                         |  Obj C       |  Obj C       | Msg Ptr      | Msg Size     | TS           |
*                                         +--------------+--------------+--------------+--------------+--------------+
*                                         |  Obj D       |  0           | 0            | 0            | 0            |
*                                         +--------------+--------------+--------------+--------------+--------------+
*                                         |  Obj E       |  0           | 0            | 0            | 0            |
*                                         +--------------+--------------+--------------+--------------+--------------+
*                                         |  Obj F       |  0           | 0            | 0            | 0            |
*                                         +--------------+--------------+--------------+--------------+--------------+
*
*
* Arguments  : p_obj        is a pointer to the object being posted to
*              -----
*
*              p_tcb        is the OS_TCB of the task receiving the signal or the message
*              -----
*
*              p_void       is the actual message (assuming posting to a message queue).  A NULL pointer otherwise.
*
*              msg_size     is the size of the message sent (if posted to a message queue)
*
*              ts           is the time stamp of when the post occurred
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_Post1 (OS_PEND_OBJ  *p_obj,
                OS_TCB       *p_tcb,
                void         *p_void,
                OS_MSG_SIZE   msg_size,
                CPU_TS        ts)
{
    OS_OBJ_QTY      n_pend_list;                                    /* Number of pend lists                           */
    OS_PEND_DATA   *p_pend_data;


    //取出任务等待的内核对象数据指向OSSemPend()时的那个参数 
    //虽然p_pend_data定义的时候是一个函数内的临时变量但有UCOS的进
    //程调度机制就知道这个临时变量的寿命可以保留到等待结束且被巧妙的释放 
    p_pend_data = p_tcb->PendDataTblPtr;                            /* Point to the first OS_PEND_DATA to remove      */
    //任务等待内核对象的个数是? 
    n_pend_list = p_tcb->PendDataTblEntries;                        /* Get number of entries in the table             */

    while (n_pend_list > (OS_OBJ_QTY)0) {                           /* Mark posted object in OS_PEND_DATA table       */
        //查找和等待当前内核的pend_data,一般任务就只有一个
        //因此这个循环一般情况也是执行一次,任务等待多个内核对象时就会循环至少一次 
        if (p_obj == p_pend_data->PendObjPtr) {                     /* Did we find the object posted to?              */
            p_pend_data->RdyObjPtr  = p_obj;                        /* Yes, indicate the object in the .RdyObjPtr     */
            p_pend_data->RdyMsgPtr  = p_void;                       /*      store the message posted                  */
            p_pend_data->RdyMsgSize = msg_size;                     /*      store the size of the message posted      */
            p_pend_data->RdyTS      = ts;                           /*      save the timestamp of the post            */
            break;
        }
        p_pend_data++;
        n_pend_list--;
    }
}
OS_Post1()

4.以上就是多值信号量基本工作原理,还有一些相关的操作函数有以下几个:

 强制解除等待状态

************************************************************************************************************************
*                                             ABORT WAITING ON A SEMAPHORE
*
* Description: This function aborts & readies any tasks currently waiting on a semaphore.  This function should be used
*              to fault-abort the wait on the semaphore, rather than to normally signal the semaphore via OSSemPost().
*
* Arguments  : p_sem     is a pointer to the semaphore
*
*              opt       determines the type of ABORT performed:
*
*                            OS_OPT_PEND_ABORT_1          ABORT wait for a single task (HPT) waiting on the semaphore
*                            OS_OPT_PEND_ABORT_ALL        ABORT wait for ALL tasks that are  waiting on the semaphore
*                            OS_OPT_POST_NO_SCHED         Do not call the scheduler
*
*              p_err     is a pointer to a variable that will contain an error code returned by this function.
*
*                            OS_ERR_NONE                  At least one task waiting on the semaphore was readied and
*                                                         informed of the aborted wait; check return value for the
*                                                         number of tasks whose wait on the semaphore was aborted.
*                            OS_ERR_OBJ_PTR_NULL          If 'p_sem' is a NULL pointer.
*                            OS_ERR_OBJ_TYPE              If 'p_sem' is not pointing at a semaphore
*                            OS_ERR_OPT_INVALID           If you specified an invalid option
*                            OS_ERR_PEND_ABORT_ISR        If you called this function from an ISR
*                            OS_ERR_PEND_ABORT_NONE       No task were pending
*
* Returns    : == 0          if no tasks were waiting on the semaphore, or upon error.
*              >  0          if one or more tasks waiting on the semaphore are now readied and informed.
************************************************************************************************************************
*/

#if OS_CFG_SEM_PEND_ABORT_EN > 0u
OS_OBJ_QTY  OSSemPendAbort (OS_SEM  *p_sem,
                            OS_OPT   opt,
                            OS_ERR  *p_err)
{
    OS_PEND_LIST  *p_pend_list;
    OS_TCB        *p_tcb;
    CPU_TS         ts;
    OS_OBJ_QTY     nbr_tasks;
    CPU_SR_ALLOC();



#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((OS_OBJ_QTY)0u);
    }
#endif

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0u) {             /* Not allowed to Pend Abort from an ISR                  */
       *p_err =  OS_ERR_PEND_ABORT_ISR;
        return ((OS_OBJ_QTY)0u);
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u
    if (p_sem == (OS_SEM *)0) {                             /* Validate 'p_sem'                                       */
       *p_err =  OS_ERR_OBJ_PTR_NULL;
        return ((OS_OBJ_QTY)0u);
    }
    switch (opt) {                                          /* Validate 'opt'                                         */
        //选项检查 
        case OS_OPT_PEND_ABORT_1:
        case OS_OPT_PEND_ABORT_ALL:
        case OS_OPT_PEND_ABORT_1   | OS_OPT_POST_NO_SCHED:
        case OS_OPT_PEND_ABORT_ALL | OS_OPT_POST_NO_SCHED:
             break;

        default:
            *p_err =  OS_ERR_OPT_INVALID;
             return ((OS_OBJ_QTY)0u);
    }
#endif

#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
    if (p_sem->Type != OS_OBJ_TYPE_SEM) {                   /* Make sure semaphore was created                        */
       *p_err =  OS_ERR_OBJ_TYPE;
        return ((OS_OBJ_QTY)0u);
    }
#endif

    CPU_CRITICAL_ENTER();
    //取出等待队列管理变量 
    p_pend_list = &p_sem->PendList;
    if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0u) {        /* Any task waiting on semaphore?                         */
        CPU_CRITICAL_EXIT();                                /* No                                                     */
       *p_err =  OS_ERR_PEND_ABORT_NONE;
        return ((OS_OBJ_QTY)0u);
    }

    OS_CRITICAL_ENTER_CPU_EXIT();
    nbr_tasks = 0u;
    ts        = OS_TS_GET();                                /* Get local time stamp so all tasks get the same time    */
    while (p_pend_list->NbrEntries > (OS_OBJ_QTY)0u) {
        p_tcb = p_pend_list->HeadPtr->TCBPtr;
        //解除内核对象上的一个等待任务的等待状态 
        OS_PendAbort((OS_PEND_OBJ *)((void *)p_sem),
                     p_tcb,
                     ts);
        nbr_tasks++;
        if (opt != OS_OPT_PEND_ABORT_ALL) {                 /* Pend abort all tasks waiting?                          */
            break;                                          /* No                                                     */
        }
    }
    OS_CRITICAL_EXIT_NO_SCHED();

    if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0u) {
        OSSched();                                          /* Run the scheduler                                      */
    }

   *p_err = OS_ERR_NONE;
    return (nbr_tasks);
}
#endif
OSSemPendAbort

 强制解除内核对象上的一个任务

************************************************************************************************************************
*                                                     ABORT PENDING
*
* Description: This function is called by OSxxxPendAbort() functions to abort pending on an event.
*
* Arguments  : p_obj          Is a pointer to the object to pend abort.
*              -----
*
*              p_tcb          Is a pointer to the OS_TCB of the task that we'll abort the pend for
*              -----
*
*              ts             The is a timestamp as to when the pend abort occurred
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_PendAbort (OS_PEND_OBJ  *p_obj,
                    OS_TCB       *p_tcb,
                    CPU_TS        ts)
{
    switch (p_tcb->TaskState) {
        case OS_TASK_STATE_RDY:                             /* Cannot Pend Abort a task that is ready                 */
        case OS_TASK_STATE_DLY:                             /* Cannot Pend Abort a task that is delayed               */
        case OS_TASK_STATE_SUSPENDED:                       /* Cannot Pend Abort a suspended task                     */
        case OS_TASK_STATE_DLY_SUSPENDED:                   /* Cannot Pend Abort a suspended task that was also dly'd */
             break;

        case OS_TASK_STATE_PEND:
        case OS_TASK_STATE_PEND_TIMEOUT:
            //等待做个内核对象? 
             if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {
                 //是 标记任务某一内核对象被强制解除等待状态 
                 OS_PendAbort1(p_obj,                            /* Indicate which object was pend aborted            */
                               p_tcb,
                               ts);
             }
#if (OS_MSG_EN > 0u)
             p_tcb->MsgPtr     = (void      *)0;
             p_tcb->MsgSize    = (OS_MSG_SIZE)0u;
#endif
             //
             p_tcb->TS         = ts;
             if (p_obj != (OS_PEND_OBJ *)0) {
                 //从等待列表移除 
                 OS_PendListRemove(p_tcb);                       /* Remove task from all pend lists                   */
             }
             //加入就素list 
             OS_TaskRdy(p_tcb);
             p_tcb->TaskState  = OS_TASK_STATE_RDY;              /* Task will be ready                                */
             p_tcb->PendStatus = OS_STATUS_PEND_ABORT;           /* Indicate pend was aborted                         */
             p_tcb->PendOn     = OS_TASK_PEND_ON_NOTHING;        /* Indicate no longer pending                        */
             break;

        case OS_TASK_STATE_PEND_SUSPENDED:
        case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
             if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {
                 OS_PendAbort1(p_obj,                            /* Indicate which object was pend aborted            */
                               p_tcb,
                               ts);
             }
#if (OS_MSG_EN > 0u)
             p_tcb->MsgPtr     = (void      *)0;
             p_tcb->MsgSize    = (OS_MSG_SIZE)0u;
#endif
             p_tcb->TS         = ts;
             if (p_obj != (OS_PEND_OBJ *)0) {
                 OS_PendListRemove(p_tcb);                       /* Remove task from all pend lists                   */
             }
             OS_TickListRemove(p_tcb);                           /* Cancel the timeout                                */
             p_tcb->TaskState  = OS_TASK_STATE_SUSPENDED;        /* Pend Aborted task is still suspended              */
             p_tcb->PendStatus = OS_STATUS_PEND_ABORT;           /* Indicate pend was aborted                         */
             p_tcb->PendOn     = OS_TASK_PEND_ON_NOTHING;        /* Indicate no longer pending                        */
             break;

        default:
             break;
    }
}
OS_PendAbort ()

标记某一内核对象上的任务被强制解除

************************************************************************************************************************
*                                           PEND ABORT A TASK PENDING ON MULTIPLE OBJECTS
*
* Description: This function is called when a task is pending on multiple objects and one of the objects has been pend
*              aborted.  This function needs to indicate to the caller which object was pend aborted by placing the
*              address of the object in the OS_PEND_DATA table corresponding to the pend aborted object.
*
*              For example, if the task pends on six (6) objects, the address of those 6 objects are placed in the
*              .PendObjPtr field of the OS_PEND_DATA table as shown below.  Note that the .PendDataTblEntries of the
*              OS_TCB would be set to six (6) in this case.  As shown, when the pend call returns because a task pend
*              aborted 'Obj C' then, only the one entry contains the .RdyObjPtr filled in data and the other entries
*              contains NULL pointers and zero data.
*
*              You should note that the NULL pointers are zero data values are actually filled in by the pend call.
*
*
*                                           .PendObjPtr    .RdyObjPtr     .RdyMsgPtr     .RdyMsgSize    .RdyTS
*                                         +--------------+--------------+--------------+--------------+--------------+
*              p_tcb->PendDataTblPtr  ->  |  Obj A       |  0           | 0            | 0            | 0            |
*                                         +--------------+--------------+--------------+--------------+--------------+
*                                         |  Obj B       |  0           | 0            | 0            | 0            |
*                                         +--------------+--------------+--------------+--------------+--------------+
*                                         |  Obj C       |  Obj C       | 0            | 0            | TS           |
*                                         +--------------+--------------+--------------+--------------+--------------+
*                                         |  Obj D       |  0           | 0            | 0            | 0            |
*                                         +--------------+--------------+--------------+--------------+--------------+
*                                         |  Obj E       |  0           | 0            | 0            | 0            |
*                                         +--------------+--------------+--------------+--------------+--------------+
*                                         |  Obj F       |  0           | 0            | 0            | 0            |
*                                         +--------------+--------------+--------------+--------------+--------------+
*
*
* Arguments  : p_obj        is a pointer to the object being pend aborted to
*              -----
*
*              p_tcb        is a pointer to the OS_TCB of the task that we'll abort he pend for
*              -----
*
*              ts           is the time stamp of when the pend abort occurred
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_PendAbort1 (OS_PEND_OBJ  *p_obj,
                     OS_TCB       *p_tcb,
                     CPU_TS        ts)
{
    OS_OBJ_QTY      n_pend_list;                                    /* Number of pend lists                           */
    OS_PEND_DATA   *p_pend_data;



    p_pend_data = p_tcb->PendDataTblPtr;                            /* Point to the first OS_PEND_DATA to remove      */
    n_pend_list = p_tcb->PendDataTblEntries;                        /* Get number of entries in the table             */

    while (n_pend_list > (OS_OBJ_QTY)0) {                           /* Mark posted object in OS_PEND_DATA table       */
        if (p_obj == p_pend_data->PendObjPtr) {                     /* Did we find the object pend aborted?           */
            p_pend_data->RdyObjPtr = p_obj;                         /* Yes, indicate the object in the .RdyObjPtr     */
            p_pend_data->RdyTS     = ts;                            /*      save the timestamp of the pend abort      */
            break;
        }
        p_pend_data++;
        n_pend_list--;
    }
}
OS_PendAbort1 ()

 删除信号量

************************************************************************************************************************
*                                                  DELETE A SEMAPHORE
*
* Description: This function deletes a semaphore.
*
* Arguments  : p_sem         is a pointer to the semaphore to delete
*
*              opt           determines delete options as follows:
*
*                                OS_OPT_DEL_NO_PEND          Delete semaphore ONLY if no task pending
*                                OS_OPT_DEL_ALWAYS           Deletes the semaphore even if tasks are waiting.
*                                                            In this case, all the tasks pending will be readied.
*
*              p_err         is a pointer to a variable that will contain an error code returned by this function.
*
*                                OS_ERR_NONE                 The call was successful and the semaphore was deleted
*                                OS_ERR_DEL_ISR              If you attempted to delete the semaphore from an ISR
*                                OS_ERR_OBJ_PTR_NULL         If 'p_sem' is a NULL pointer.
*                                OS_ERR_OBJ_TYPE             If 'p_sem' is not pointing at a semaphore
*                                OS_ERR_OPT_INVALID          An invalid option was specified
*                                OS_ERR_TASK_WAITING         One or more tasks were waiting on the semaphore
*
* Returns    : == 0          if no tasks were waiting on the semaphore, or upon error.
*              >  0          if one or more tasks waiting on the semaphore are now readied and informed.
*
* Note(s)    : 1) This function must be used with care.  Tasks that would normally expect the presence of the semaphore
*                 MUST check the return code of OSSemPend().
*              2) OSSemAccept() callers will not know that the intended semaphore has been deleted.
*              3) Because ALL tasks pending on the semaphore will be readied, you MUST be careful in applications where
*                 the semaphore is used for mutual exclusion because the resource(s) will no longer be guarded by the
*                 semaphore.
************************************************************************************************************************
*/

#if OS_CFG_SEM_DEL_EN > 0u
OS_OBJ_QTY  OSSemDel (OS_SEM  *p_sem,
                      OS_OPT   opt,
                      OS_ERR  *p_err)
{
    OS_OBJ_QTY     cnt;
    OS_OBJ_QTY     nbr_tasks;
    OS_PEND_DATA  *p_pend_data;
    OS_PEND_LIST  *p_pend_list;
    OS_TCB        *p_tcb;
    CPU_TS         ts;
    CPU_SR_ALLOC();



#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return ((OS_OBJ_QTY)0);
    }
#endif

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* Not allowed to delete a semaphore from an ISR          */
       *p_err = OS_ERR_DEL_ISR;
        return ((OS_OBJ_QTY)0);
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u
    if (p_sem == (OS_SEM *)0) {                             /* Validate 'p_sem'                                       */
       *p_err = OS_ERR_OBJ_PTR_NULL;
        return ((OS_OBJ_QTY)0);
    }
    switch (opt) {                                          /* Validate 'opt'                                         */
        //选项和法性检查 
        case OS_OPT_DEL_NO_PEND:
        case OS_OPT_DEL_ALWAYS:
             break;

        default:
            *p_err = OS_ERR_OPT_INVALID;
             return ((OS_OBJ_QTY)0);
    }
#endif

#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
    if (p_sem->Type != OS_OBJ_TYPE_SEM) {                   /* Make sure semaphore was created                        */
       *p_err = OS_ERR_OBJ_TYPE;
        return ((OS_OBJ_QTY)0);
    }
#endif

    CPU_CRITICAL_ENTER();
    //取出等待队列 
    p_pend_list = &p_sem->PendList;
    //取出等待队列数量 
    cnt         = p_pend_list->NbrEntries;
    nbr_tasks   = cnt;
    switch (opt) {
        //无等待任务 
        case OS_OPT_DEL_NO_PEND:                            /* Delete semaphore only if no task waiting               */
             if (nbr_tasks == (OS_OBJ_QTY)0) {
#if OS_CFG_DBG_EN > 0u
                 OS_SemDbgListRemove(p_sem);
#endif
                 //信号量-- 
                 OSSemQty--;
                 //清除信号量 
                 OS_SemClr(p_sem);
                 CPU_CRITICAL_EXIT();
                *p_err = OS_ERR_NONE;
             } else {
                 CPU_CRITICAL_EXIT();
                *p_err = OS_ERR_TASK_WAITING;
             }
             break;

        case OS_OPT_DEL_ALWAYS:                             /* Always delete the semaphore                            */
             //有任务等待依然删除 
             OS_CRITICAL_ENTER_CPU_EXIT();
             ts = OS_TS_GET();                              /* Get local time stamp so all tasks get the same time    */
             while (cnt > 0u) {                             /* Remove all tasks on the pend list                      */
                 p_pend_data = p_pend_list->HeadPtr;
                 p_tcb       = p_pend_data->TCBPtr;
                 //删除一个信号量即删除一个等待任务 
                 OS_PendObjDel((OS_PEND_OBJ *)((void *)p_sem),
                               p_tcb,
                               ts);
                 cnt--;
             }
#if OS_CFG_DBG_EN > 0u
             OS_SemDbgListRemove(p_sem);
#endif
             OSSemQty--;
             OS_SemClr(p_sem);
             OS_CRITICAL_EXIT_NO_SCHED();
             OSSched();                                     /* Find highest priority task ready to run                */
            *p_err = OS_ERR_NONE;
             break;

        default:
             CPU_CRITICAL_EXIT();
            *p_err = OS_ERR_OPT_INVALID;
             break;
    }
    return ((OS_OBJ_QTY)nbr_tasks);
}
#endif
OSSemDel ()

 信号量清除

************************************************************************************************************************
*                                           CLEAR THE CONTENTS OF A SEMAPHORE
*
* Description: This function is called by OSSemDel() to clear the contents of a semaphore
*

* Argument(s): p_sem      is a pointer to the semaphore to clear
*              -----
*
* Returns    : none
*
* Note(s)    : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
************************************************************************************************************************
*/

void  OS_SemClr (OS_SEM  *p_sem)
{
    p_sem->Type    = OS_OBJ_TYPE_NONE;                      /* Mark the data structure as a NONE                      */
    p_sem->Ctr     = (OS_SEM_CTR)0;                         /* Set semaphore value                                    */
    p_sem->TS      = (CPU_TS    )0;                         /* Clear the time stamp                                   */
    p_sem->NamePtr = (CPU_CHAR *)((void *)"?SEM");
    OS_PendListInit(&p_sem->PendList);                      /* Initialize the waiting list                            */
}
OS_SemClr ()

 删除信号量节点

************************************************************************************************************************
*                                READY A TASK THAT WAS PENDING ON AN OBJECT BEING DELETED
*
* Description: This function is called to make a task ready-to-run because an object is being deleted
*
* Arguments  : p_obj          is a pointer to the object being deleted
*              -----
*
*              p_tcb          is a pointer to the OS_TCB of the task to make ready-to-run
*              -----
*
*              ts             is a timestamp to indicate when the object was deleted
*
* Returns    : none
*
* Note(s)    : This function is INTERNAL to uC/OS-III and your application should not call it.
************************************************************************************************************************
*/

void  OS_PendObjDel (OS_PEND_OBJ  *p_obj,
                     OS_TCB       *p_tcb,
                     CPU_TS        ts)
{
    switch (p_tcb->TaskState) {
        case OS_TASK_STATE_RDY:                                  /* These states should never occur                   */
        case OS_TASK_STATE_DLY:
        case OS_TASK_STATE_SUSPENDED:
        case OS_TASK_STATE_DLY_SUSPENDED:
             break;

        case OS_TASK_STATE_PEND:
        case OS_TASK_STATE_PEND_TIMEOUT:
            //等待多个内核对象 ?
             if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {
                //yes
                //等待对象删除后加入就绪 
                 OS_PendObjDel1(p_obj,                           /* Indicate which object was pend aborted            */
                                p_tcb,
                                ts);
             }
#if (OS_MSG_EN > 0u)
             p_tcb->MsgPtr     = (void *)0;
             p_tcb->MsgSize    = (OS_MSG_SIZE)0u;
#endif
            // No
             p_tcb->TS         = ts;
            //将任务从等待list删除 
             OS_PendListRemove(p_tcb);                           /* Remove task from all wait lists                   */
            //将任务从加入就绪list
             OS_TaskRdy(p_tcb);
             p_tcb->TaskState  = OS_TASK_STATE_RDY;              /* Task is readied because object is deleted         */
             p_tcb->PendStatus = OS_STATUS_PEND_DEL;             
             p_tcb->PendOn     = OS_TASK_PEND_ON_NOTHING;
             break;

        case OS_TASK_STATE_PEND_SUSPENDED:
        case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
             if (p_tcb->PendOn == OS_TASK_PEND_ON_MULTI) {
                 OS_PendObjDel1(p_obj,                           /* Indicate which object was pend aborted            */
                                p_tcb,
                                ts);
             }
#if (OS_MSG_EN > 0u)
             p_tcb->MsgPtr     = (void      *)0;
             p_tcb->MsgSize    = (OS_MSG_SIZE)0u;
#endif
             p_tcb->TS         = ts;
             OS_TickListRemove(p_tcb);                           /* Cancel the timeout                                */
             OS_PendListRemove(p_tcb);                           /* Remove task from all wait lists                   */
             p_tcb->TaskState  = OS_TASK_STATE_SUSPENDED;        /* Task needs to remain suspended                    */
             p_tcb->PendStatus = OS_STATUS_PEND_DEL;             
             p_tcb->PendOn     = OS_TASK_PEND_ON_NOTHING;        /* Indicate no longer pending                        */
             break;

        default:
             break;
    }
}
OS_PendObjDel ()

 设置信号量值

************************************************************************************************************************
*                                                    SET SEMAPHORE
*
* Description: This function sets the semaphore count to the value specified as an argument.  Typically, this value
*              would be 0 but of course, we can set the semaphore to any value.
*
*              You would typically use this function when a semaphore is used as a signaling mechanism
*              and, you want to reset the count value.
*
* Arguments  : p_sem     is a pointer to the semaphore
*
*              cnt       is the new value for the semaphore count.  You would pass 0 to reset the semaphore count.
*
*              p_err     is a pointer to a variable that will contain an error code returned by this function.
*
*                            OS_ERR_NONE           The call was successful and the semaphore value was set.
*                            OS_ERR_OBJ_PTR_NULL   If 'p_sem' is a NULL pointer.
*                            OS_ERR_OBJ_TYPE       If 'p_sem' is not pointing to a semaphore.
*                            OS_ERR_TASK_WAITING   If tasks are waiting on the semaphore.
*
* Returns    : None
************************************************************************************************************************
*/

#if OS_CFG_SEM_SET_EN > 0u
void  OSSemSet (OS_SEM      *p_sem,
                OS_SEM_CTR   cnt,
                OS_ERR      *p_err)
{
    OS_PEND_LIST  *p_pend_list;
    CPU_SR_ALLOC();



#ifdef OS_SAFETY_CRITICAL
    if (p_err == (OS_ERR *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
        return;
    }
#endif

#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
    if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* Can't call this function from an ISR                   */
       *p_err = OS_ERR_SET_ISR;
        return;
    }
#endif

#if OS_CFG_ARG_CHK_EN > 0u
    if (p_sem == (OS_SEM *)0) {                             /* Validate 'p_sem'                                       */
       *p_err = OS_ERR_OBJ_PTR_NULL;
        return;
    }
#endif

#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
    if (p_sem->Type != OS_OBJ_TYPE_SEM) {                   /* Make sure semaphore was created                        */
       *p_err = OS_ERR_OBJ_TYPE;
        return;
    }
#endif

   *p_err = OS_ERR_NONE;
    CPU_CRITICAL_ENTER();
    //´Ë״̬²»¿ÉÄÜÓÐÈÎÎñÔڵȴýÐźÅÁ¿Ö±½ÓÐÞ¸Ä 
    if (p_sem->Ctr > (OS_SEM_CTR)0) {                       /* See if semaphore already has a count                   */
        p_sem->Ctr = cnt;                                   /* Yes, set it to the new value specified.                */
    } else {
        //·ñÔò   ÅжÏÊDz»ÓÐÈÎÎñÔڵȴýÐźÅÁ¿£¿ 
        p_pend_list = &p_sem->PendList;                     /* No                                                     */
        if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0) {     /*      See if task(s) waiting?                           */
           //No  Ö±½ÓÐÞ¸Ä+ 
            p_sem->Ctr = cnt;                               /*      No, OK to set the value                           */
        } else {
         //Yes ²»ÐíÐÞ¸ÄÐźÅÁ¿Öµ 
           *p_err      = OS_ERR_TASK_WAITING;
        }
    }
    CPU_CRITICAL_EXIT();
}
#endif
OSSemSet ()

 信号量有很多个操作,但都是对数据结构的操作,因此理清数据结构就知道各个函数都是如何工作的,多值信号量在请求任务内调用在无法请求到信号量时会将当前任务加入等待列表,在post信号量时在等待队列里找到对应的发布任务将其就绪,对于等待列表则是一个按照任务优先级排序的双向链表。

 

posted @ 2017-11-28 13:32  Little_Village  阅读(873)  评论(0编辑  收藏  举报