ucosii任务间的同步与通信

ucosii任务间的同步和通信都可以通过“事件”的方式来搞定,这个事件包括:信号量、互斥量、邮箱和消息队列

其中信号量和互斥量用于同步,邮箱和消息队列可用于通信。

任务中有任务控制块,同样,在“事件”中有事件控制块ECB

typedef struct os_event {
    INT8U    OSEventType;                    /* Type of event control block (see OS_EVENT_TYPE_xxxx)    */
    void    *OSEventPtr;                     /* Pointer to message or queue structure                   */
    INT16U   OSEventCnt;                     /* Semaphore Count (not used if other EVENT type)          */
    OS_PRIO  OSEventGrp;                     /* Group corresponding to tasks waiting for event to occur */
    OS_PRIO  OSEventTbl[OS_EVENT_TBL_SIZE];  /* List of tasks waiting for event to occur                */

#if OS_EVENT_NAME_EN > 0u
    INT8U   *OSEventName;
#endif
} OS_EVENT;

OSEventType:事件类型,包括

                         #define  OS_EVENT_TYPE_UNUSED           0u
                         #define  OS_EVENT_TYPE_MBOX             1u         //邮箱
                         #define  OS_EVENT_TYPE_Q                2u             //消息
                         #define  OS_EVENT_TYPE_SEM              3u           //信号量
                         #define  OS_EVENT_TYPE_MUTEX            4u        //互斥量

                         #define  OS_EVENT_TYPE_FLAG             5u

OSEventPtr:指向消息的指针

OSEventCnt:信号量值,只有在信号量中才使用

OSEventGrp:等待事件的任务组,作用相当于OSRdyGrp,和线面的任务等代表配合使用

OSEventTbl:任务等待表,跟任务就绪表的使用方法是一样的,有哪个任务在等待这个“事件”,就把相应的bit位置1



把一个任务置于等待状态调用void  OS_EventTaskWait (OS_EVENT *pevent),信号量、互斥量、邮箱和消息队列的等待函数OSXXXPend()都会调用它。

如果一个等待任务具备了运行条件,那么就使它进入就绪状态,这时候调用的是INT8U  OS_EventTaskRdy(),就是事件控制块中的任务等待表相应位清零,就绪任务表相应位置0

系统在初始化的时候也会创建一个空事件控制块链表,作用和空任务控制块链表一样,有ECB被创建时,就从空事件控制块链表中拿走一个,删除一个事件时,再将事件控制块归还空事件控制块链表。



信号量及其操作

1、创建信号量

函数原型:OS_EVENT  *OSSemCreate (INT16U cnt)

参数:cnt,初始化信号量的值

返回值:返回一个ECB控制块

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



    if (OSIntNesting > 0u) {                               /* 中断中禁止创建信号量               */
        return ((OS_EVENT *)0);                            /* ... can't CREATE from an ISR             */
    }
    OS_ENTER_CRITICAL();
    pevent = OSEventFreeList;                              /* 从空ECB链表中获取一个ECBGet 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) {                         /* Get an event control block               */
        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);                      /* Initialize to 'nobody waiting' on sem.等待列表清零   */
    }
    return (pevent);
}



2、获取信号量

函数原型:void  OSSemPend (OS_EVENT  *pevent,
                 INT32U     timeout,

                 INT8U     *perr)

参数:pevent:ECB指针

          timerout:超时时间,0表示无线等待

         perr:返回的错误信息

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) {
        OS_SAFETY_CRITICAL_EXCEPTION();
    }
#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) {                    /* If sem. is positive, resource available ...   */
        pevent->OSEventCnt--;                         /* ... decrement semaphore only if positive.     */
        OS_EXIT_CRITICAL();                            /*信号量值大于0,也就是信号量可以获取成功,直接返回继续执行*/
        *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);                         /* 把当前任务放到等待列表当中Suspend task until event or timeout occurs    */
    OS_EXIT_CRITICAL();
    OS_Sched();                                       /* Find next highest priority task ready         */
    OS_ENTER_CRITICAL();
    switch (OSTCBCur->OSTCBStatPend) {                /* See if we timed-out or aborted                */
        case OS_STAT_PEND_OK:                         /*信号量被释放了,获取成功了*/
             *perr = OS_ERR_NONE;
             break;

        case OS_STAT_PEND_ABORT:                      /*信号量被中止了*/
             *perr = OS_ERR_PEND_ABORT;               /* Indicate that we aborted                      */
             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;    /* Clear event pointers                          */
#if (OS_EVENT_MULTI_EN > 0u)
    OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
    OS_EXIT_CRITICAL();
}

3、释放信号量

函数原型:INT8U  OSSemPost (OS_EVENT *pevent)

参数:pevent:时间指针

返回值:OS_ERR_NONE 成功

             OS_ERR_SEM_OVF 信号量的值溢出

             OS_ERR_EVENT_TYPE 传入的参数不是一个信号量

            OS_ERR_PEVENT_NULL 传入参数为NULL

释放信号量就是要把信号量的值加1,在这之前,先检查是否有任务在等待这个信号量,如果有,则去切换到高优先级任务。(其实我不理解为啥要先去切换任务,再去把信号量的值++,先++再切换任务ok不呢,请各位网友指教哦)

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) {                /* Make sure semaphore will not overflow         */
        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);
}

4 信号删除

函数原型:OS_EVENT  *OSSemDel (OS_EVENT  *pevent,
                     INT8U      opt,
                     INT8U     *perr)

参数:pevent 事件指针

          opt:OS_DEL_NO_PEND 没有任务处于此信号量的等待表中时方可删除

                   OS_DEL_ALWAYS 直接删除

perr:成功返回OS_ERR_NONE

         从中断中删除信号量返回OS_ERR_DEL_ISR

        opt错误返回OS_ERR_INVALID_OPT

         还有任务在等待信号量OS_ERR_TASK_WAITING

        传递事件指针错误,不是信号量指针返回OS_ERR_EVENT_TYPE

        事件指针为空OS_ERR_PEVENT_NULL

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();
    }
#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;                           /* Yes                                      */
    } 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返回到空ECB链表  */
                 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_OK);
             }
#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);
}

互斥量及其操作

互斥量与信号量最大的不同是,互斥量可以防止出现优先级反转的问题。。



看一下互斥量如何防止出现优先级反转的


一个互斥量的结构如下,通过OSEventCnt来实现防止优先级反转功能。分成低八位和高八位,低八位为0XFF表示资源可以获得,否则获取资源失败,高八位表示的是要提升的优先级


1、创建互斥量

函数原型:OS_EVENT  *OSMutexCreate (INT8U   prio,

                          INT8U  *perr)

参数:1、优先级,必须保证优先级是未使用的

         2、错误码

疑惑:1、为何要自己设定这个优先级,为何不直接将使用互斥量任务的优先级复制到ECB中呢?2、为何不能使用已存在的优先级呢?欢迎大神们解答

返回值:成功返回一个ECB指针

OS_EVENT  *OSMutexCreate (INT8U   prio,
                          INT8U  *perr)
{
    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
    if (perr == (INT8U *)0) {
        OS_SAFETY_CRITICAL_EXCEPTION();
    }
#endif

#ifdef OS_SAFETY_CRITICAL_IEC61508
    if (OSSafetyCriticalStartFlag == OS_TRUE) {
        OS_SAFETY_CRITICAL_EXCEPTION();
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (prio >= OS_LOWEST_PRIO) {                          /* Validate PIP                             */
        *perr = OS_ERR_PRIO_INVALID;
        return ((OS_EVENT *)0);
    }
#endif
    if (OSIntNesting > 0u) {                               /* See if called from ISR ...               */
        *perr = OS_ERR_CREATE_ISR;                         /* ... can't CREATE mutex from an ISR       */
        return ((OS_EVENT *)0);
    }
    OS_ENTER_CRITICAL();                                  /*设定的优先级必须是不存在的(why?)*/
    if (OSTCBPrioTbl[prio] != (OS_TCB *)0) {               /* Mutex priority must not already exist    */
        OS_EXIT_CRITICAL();                                /* Task already exist at priority ...       */
        *perr = OS_ERR_PRIO_EXIST;                         /* ... inheritance priority                 */
        return ((OS_EVENT *)0);
    }
    OSTCBPrioTbl[prio] = OS_TCB_RESERVED;                  /* Reserve the table entry                  */
    pevent             = OSEventFreeList;                  /* Get next free event control block        */
    if (pevent == (OS_EVENT *)0) {                         /* See if an ECB was available              */
        OSTCBPrioTbl[prio] = (OS_TCB *)0;                  /* No, Release the table entry              */
        OS_EXIT_CRITICAL();
        *perr              = OS_ERR_PEVENT_NULL;           /* No more event control blocks             */
        return (pevent);
    }
    OSEventFreeList        = (OS_EVENT *)OSEventFreeList->OSEventPtr;   /* Adjust the free list        */
    OS_EXIT_CRITICAL();
    pevent->OSEventType    = OS_EVENT_TYPE_MUTEX;              /*对ECB进行赋值*/
    pevent->OSEventCnt     = (INT16U)((INT16U)prio << 8u) | OS_MUTEX_AVAILABLE; /* Resource is avail.  */
    pevent->OSEventPtr     = (void *)0;                                 /* No task owning the mutex    */
#if OS_EVENT_NAME_EN > 0u
    pevent->OSEventName    = (INT8U *)(void *)"?";
#endif
    OS_EventWaitListInit(pevent);
    *perr                  = OS_ERR_NONE;
    return (pevent);
}

消息队列

消息队列可以传递多条消息,消息队列由3部分组成:ECB,消息队列和消息。

消息队列比上述几个多了两个数据结构:队列控制块OS_Q和一个指针数组MsgTbl[],数组内的指针指向消息,这个数组是自己创建的

这几项的关系如下,OSEventPtr指向了消息控制块,消息控制块中的变量又指向数组MsgTbl的各个部分


消息控制块中各个变量含义:


用户创建的消息指针数组中存放的是传输消息的地址,这个数组在使用的时候是一个环形buffer,OSQOut指向的是将要被取出的消息地址。OSQIn存放的则是下一个能被存放消息的地址。

1、创建消息队列

函数原型:OS_EVENT  *OSQCreate (void    **start,

                      INT16U    size)

参数:start,消息指针数组的起始地址

          size:消息队列长度

返回值:事件指针

OS_EVENT  *OSQCreate (void    **start,
                      INT16U    size)
{
    OS_EVENT  *pevent;
    OS_Q      *pq;
#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();
    }
#endif

    if (OSIntNesting > 0u) {                     /* See if called from ISR ...                         */
        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) {               /* See if we have an event control block              */
        OS_ENTER_CRITICAL();
        pq = OSQFreeList;                        /* Get a free queue control block                     */
        if (pq != (OS_Q *)0) {                   /* Were we able to get a queue control block ?        */
            OSQFreeList            = OSQFreeList->OSQPtr; /* Yes, Adjust free list pointer to next free*/
            OS_EXIT_CRITICAL();
            pq->OSQStart           = start;               /*      Initialize the queue                 */
            pq->OSQEnd             = &start[size];
            pq->OSQIn              = start;
            pq->OSQOut             = start;
            pq->OSQSize            = size;
            pq->OSQEntries         = 0u;
            pevent->OSEventType    = OS_EVENT_TYPE_Q;
            pevent->OSEventCnt     = 0u;
            pevent->OSEventPtr     = pq;
#if OS_EVENT_NAME_EN > 0u
            pevent->OSEventName    = (INT8U *)(void *)"?";
#endif
            OS_EventWaitListInit(pevent);                 /*      Initalize the wait list              */
        } else {
            pevent->OSEventPtr = (void *)OSEventFreeList; /* No,  Return event control block on error  */
            OSEventFreeList    = pevent;
            OS_EXIT_CRITICAL();
            pevent = (OS_EVENT *)0;
        }
    }
    return (pevent);
}








猜你喜欢

转载自blog.csdn.net/u012142460/article/details/79416454