Ucos源码分析------邮箱与队列

Ucos源码分析

1.Ucos源码分析------任务控制块与任务调度
2.Ucos源码分析------事件控制块与事件控制
3.Ucos源码分析------信号量
4.Ucos源码分析------邮箱与队列
5.Ucos源码分析------事件标志组
6.Ucos源码分析------内存管理
7.Ucos源码分析------临界区与中断管理
8.Ucos源码分析------OS启动
9.Ucos总结



邮箱和队列都是用来进行消息传递的,他们的区别是:
邮箱只能传递一个消息, 队列可以传递多个消息。

1.消息队列控制块与环形队列

消息队列控制块的基本成员如下

控制块成员成员类型
struct os_q *OSQPtr队列控制块指针队列控制块连接下一个控制块
void **OSQEnd指向地址的指针队列起始的地址
void **OSQStart指向地址的指针队列终止的地址
void **OSQIn指向地址的指针队列要插入的地址
void **OSQOut指向地址的指针队列要输出的地址
OSQSizeint队列大小
OSQEntriesint当前队列大小

消息队列是一个环形队列缓冲区,通过对控制块也可以看出,队列中中存放的是地址。
事情10个大小的队列,队列的真正大小是11,有一个空的没有用到没以队列起始与终止的区分。

请添加图片描述

2.队列的创建与删除

创建队列
OS_EVENT *OSQCreate (void **start, INT16U size)
参数
  start :消息内存区的基地址,消息内存区是一个指针数组。
  Size :消息内存区的大小。

OS_EVENT  *OSQCreate (void **start, INT16U size)
{
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr;
#endif
    //事件控制块
    OS_EVENT  *pevent;
    //队列控制块
    OS_Q      *pq;

    //不能在中断调用
    if (OSIntNesting > 0) {                      /* 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             */
        //空闲事件控制链表的表头指针后移1个
        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 ?        */
            //空闲队列控制链表的表头指针后移1个
            OSQFreeList         = OSQFreeList->OSQPtr;    /* Yes, Adjust free list pointer to next free*/
            OS_EXIT_CRITICAL();
            //队列控制块初始化
            pq->OSQStart        = start;                  /*      Initialize the queue                 */
            //start[size] 如果传入size为10 则为start[10]
            //在循环队列中没有使用
            pq->OSQEnd          = &start[size];
            pq->OSQIn           = start;
            pq->OSQOut          = start;
            pq->OSQSize         = size;
            pq->OSQEntries      = 0;
            //事件控制块初始化
            pevent->OSEventType = OS_EVENT_TYPE_Q;
            pevent->OSEventCnt  = 0;
            pevent->OSEventPtr  = pq;
            //初始化事件等待列表
            OS_EventWaitListInit(pevent);                 /*      Initalize the wait list              */
        } else {
            //pq获取失败,归还事件控制块
            pevent->OSEventPtr = (void *)OSEventFreeList; /* No,  Return event control block on error  */
            OSEventFreeList    = pevent;
            OS_EXIT_CRITICAL();
            pevent = (OS_EVENT *)0;
        }
    }
    return (pevent);
}

队列的删除与信号量的删除查不多,多了一个将队列控制块归还给队列控制块链表
OS_EVENT *OSQDel (OS_EVENT *pevent, INT8U opt, INT8U *err)

//归还队列控制块归到队列控制块链表
pq                  = (OS_Q *)pevent->OSEventPtr;  /* Return OS_Q to free list        */
pq->OSQPtr          = OSQFreeList;
OSQFreeList         = pq;
pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
//归还事件控制块归到事件控制块链表
pevent->OSEventPtr  = OSEventFreeList;    /* Return Event Control Block to free list  */
OSEventFreeList     = pevent;             /* Get next free event control block        */

3.消息获取–阻塞与无阻塞

void *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
参数:
  pevent :是指向即将接受消息的队列的指针, 由OSQCreate()创建。
  Timeout :最大等待时长,用以系统超时判断
  Err 是指向包含错误码的变量的指针。
返回值: void *(msg)
    如果队列缓冲区有消息,msg从队列中获取
    如果队列缓冲区无消息,阻塞调度后,msg从 TCB 的 OSTCBMsg 获取

void  *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
{
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr;
#endif
    //传递消息的地址
    void      *msg;
    OS_Q      *pq;
    //不能在中断中调用
    if (OSIntNesting > 0) {                      /* See if called from ISR ...                         */
        *err = OS_ERR_PEND_ISR;                  /* ... can't PEND from an ISR                         */
        return ((void *)0);
    }
#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {               /* Validate 'pevent'                                  */
        *err = OS_ERR_PEVENT_NULL;
        return ((void *)0);
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_Q) {/* Validate event block type                          */
        *err = OS_ERR_EVENT_TYPE;
        return ((void *)0);
    }
#endif
    OS_ENTER_CRITICAL();
    //获取当前事件的队列指针 
    //pevent->OSEventPtr是Void * 需要强制转换为OS_Q *
    pq = (OS_Q *)pevent->OSEventPtr;             /* Point at queue control block                       */
//如果队列缓冲区有消息
    if (pq->OSQEntries > 0) {                    /* See if any messages in the queue                   */
        //队列缓冲区获取消息指针
        msg = *pq->OSQOut++;                     /* Yes, extract oldest message from the queue         */
        //队列缓冲区消息数目减少
        pq->OSQEntries--;                        /* Update the number of entries in the queue          */
        //环形队列终止判断
        if (pq->OSQOut == pq->OSQEnd) {          /* Wrap OUT pointer if we are at the end of the queue */
            pq->OSQOut = pq->OSQStart;
        }
        OS_EXIT_CRITICAL();
        *err = OS_NO_ERR;
        //返回消息指针
        return (msg);                            /* Return message received                            */
    }
//如果队列缓冲区没有消息
    // 1.任务状态等待事件置位
    OSTCBCur->OSTCBStat |= OS_STAT_Q;            /* Task will have to pend for a message to be posted  */
    OSTCBCur->OSTCBDly   = timeout;              /* Load timeout into TCB                              */
    //2.任务从就绪表移除;3.任务加入事件等待表
    OS_EventTaskWait(pevent);                    /* Suspend task until event or timeout occurs         */
    OS_EXIT_CRITICAL();
    OS_Sched();                                  /* Find next highest priority task ready to run       */
    OS_ENTER_CRITICAL();
    //获取消息指针   此时消息是在OSTCBCur->OSTCBMsg中
    msg = OSTCBCur->OSTCBMsg;
    //判断是否成功获取消息
    if (msg != (void *)0) {                      /* Did we get a message?                              */
        OSTCBCur->OSTCBMsg      = (void *)0;     /* Extract message from TCB (Put there by QPost)      */
        OSTCBCur->OSTCBStat     = OS_STAT_RDY;
        OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* No longer waiting for event                        */
        OS_EXIT_CRITICAL();
        *err                    = OS_NO_ERR;
        return (msg);                            /* Return message received                            */
    }
    //等待超时判断
    OS_EventTO(pevent);                          /* Timed out                                          */
    OS_EXIT_CRITICAL();
    *err = OS_TIMEOUT;                           /* Indicate a timeout occured                         */
    return ((void *)0);                          /* No message received                                */
}

//无阻塞的获取消息
void *OSQAccept (OS_EVENT *pevent)
返回值
  如果队列有消息:返回消息指针
  如果队列无消息:返回void * 0

void  *OSQAccept (OS_EVENT *pevent)
{
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr;
#endif
    //消息指针
    void      *msg;
    //队列控制块
    OS_Q      *pq;


#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {               /* Validate 'pevent'                                  */
        return ((void *)0);
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_Q) {/* Validate event block type                          */
        return ((void *)0);
    }
#endif
    OS_ENTER_CRITICAL();
    //获取事件的队列控制块
    pq = (OS_Q *)pevent->OSEventPtr;             /* Point at queue control block                       */
//如果队列有消息
    if (pq->OSQEntries > 0) {                    /* See if any messages in the queue                   */
        //获取队列中的消息  
        msg = *pq->OSQOut++;                     /* Yes, extract oldest message from the queue         */
        //队列中的消息数目减一
        pq->OSQEntries--;                        /* Update the number of entries in the queue          */
        //判断队列消息被获取后,是否到达了队列终点
        if (pq->OSQOut == pq->OSQEnd) {          /* Wrap OUT pointer if we are at the end of the queue */
            pq->OSQOut = pq->OSQStart;
        }
    }
//如果队列有消息     
    else {
        msg = (void *)0;                         /* Queue is empty                                     */
    }
    OS_EXIT_CRITICAL();
    return (msg);                                /* Return message received (or NULL)                  */
}

4.消息释放–3种

INT8U OSQPost:将消息指针放在队头,-----后入后出
INT8U OSQPostFront:将消息指针放在队尾,-----后入先出
OSQPostOpt :虽然通知所有事件控制块的等待任务,但系统仅调度一次,运行最高优先级任务

OSQPost和OSQPostFront中对队列特殊情况的判断
OSQPost须在添加完信息后,判断下一个消息队列是否为最后一个。
OSQPostFront须在添加完信息前,判断当前消息队列位置是否为第一个。

4.1将消息指针放在队首-----后入后出

INT8U OSQPost (OS_EVENT *pevent, void *msg)

INT8U  OSQPost (OS_EVENT *pevent, void *msg)
{
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr;
#endif
    OS_Q      *pq;


#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {                    /* Validate 'pevent'                             */
        return (OS_ERR_PEVENT_NULL);
    }
    if (msg == (void *)0) {                           /* Make sure we are not posting a NULL pointer   */
        return (OS_ERR_POST_NULL_PTR);
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_Q) {     /* Validate event block type                     */
        return (OS_ERR_EVENT_TYPE);
    }
#endif
    OS_ENTER_CRITICAL();
//如果事件控制块的 任务等待表 有等待任务
    if (pevent->OSEventGrp != 0x00) {                 /* See if any task pending on queue              */
        //最高优先级任务从事件等待列表移除;任务事件标志msk清除;加入任务就绪表
        //任务控制块的ptcb->OSTCBMsg,指向消息指针
        OS_EventTaskRdy(pevent, msg, OS_STAT_Q);      /* Ready highest priority task waiting on event  */
        OS_EXIT_CRITICAL();
        //系统调度
        OS_Sched();                                   /* Find highest priority task ready to run       */
        return (OS_NO_ERR);
    }
//如果事件控制块的 任务等待表 无等待任务
    //消息指针入队
    pq = (OS_Q *)pevent->OSEventPtr;                  /* Point to queue control block                  */
    //判断队列是否满
    if (pq->OSQEntries >= pq->OSQSize) {              /* Make sure queue is not full                   */
        OS_EXIT_CRITICAL();
        return (OS_Q_FULL);
    }
    //(*pq->OSQIn) = msg :当前消息指针入队 一个解引用
    //(pq->OSQIn) ++     :指向消息指针的指针后移
    *pq->OSQIn++ = msg;                               /* Insert message into queue                     */
    // 队列中消息数目加一
    pq->OSQEntries++;                                 /* Update the nbr of entries in the queue        */
    //在消息入队后 判是否是最后一个消息
    if (pq->OSQIn == pq->OSQEnd) {                    /* Wrap IN ptr if we are at end of queue         */
        pq->OSQIn = pq->OSQStart;
    }
    OS_EXIT_CRITICAL();
    return (OS_NO_ERR);
}

4.2将消息指针放在队尾-----后入先出

INT8U OSQPostFront (OS_EVENT *pevent, void *msg)

// 将消息指针放在队尾,-----后入先出
INT8U  OSQPostFront (OS_EVENT *pevent, void *msg)
{
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr;
#endif
    OS_Q      *pq;


#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {                    /* Validate 'pevent'                             */
        return (OS_ERR_PEVENT_NULL);
    }
    if (msg == (void *)0) {                           /* Make sure we are not posting a NULL pointer   */
        return (OS_ERR_POST_NULL_PTR);
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_Q) {     /* Validate event block type                     */
        return (OS_ERR_EVENT_TYPE);
    }
#endif
    OS_ENTER_CRITICAL();
    if (pevent->OSEventGrp != 0x00) {                 /* See if any task pending on queue              */
        OS_EventTaskRdy(pevent, msg, OS_STAT_Q);      /* Ready highest priority task waiting on event  */
        OS_EXIT_CRITICAL();
        OS_Sched();                                   /* Find highest priority task ready to run       */
        return (OS_NO_ERR);
    }
    pq = (OS_Q *)pevent->OSEventPtr;                  /* Point to queue control block                  */
    // 判断队列是否满
    if (pq->OSQEntries >= pq->OSQSize) {              /* Make sure queue is not full                   */
        OS_EXIT_CRITICAL();
        return (OS_Q_FULL);
    }
//在消息入队前 判是否是第一个消息
    if (pq->OSQOut == pq->OSQStart) {                 /* Wrap OUT ptr if we are at the 1st queue entry */
        pq->OSQOut = pq->OSQEnd;
    }
    //将消息指针放在队尾
    pq->OSQOut--;
    *pq->OSQOut = msg;                                /* Insert message into queue                     */
    //队列中消息数目+1
    pq->OSQEntries++;                                 /* Update the nbr of entries in the queue        */
    OS_EXIT_CRITICAL();
    return (OS_NO_ERR);
}

请添加图片描述

4.3通用的信号量释放函数

INT8U OSQPostOpt (OS_EVENT *pevent, void *msg, INT8U opt)
参数:
  pevent:指向即将接受消息的消息队列的指针
  Msg 是即将实际发送给任务的消息,消息是一个指针
  opt可选参数:
    OS_POST_OPT_NONE: 消息发给 优先级最高的任务,消息后入后出(OSQPost())
    OS_POST_OPT_FRONT: 消息发给 优先级最高的任务,消息后入先出(OSQPostFront())
    OS_POST_OPT_BROADCAST: 消息发给 所有等待事件的任务 消息后入后出
    OS_POST_OPT_FRONT + OS_POST_OPT_BROADCAST:消息发给 所有等待事件的任务 消息后入先出

INT8U  OSQPostOpt (OS_EVENT *pevent, void *msg, INT8U opt)
{
#if OS_CRITICAL_METHOD == 3                      /* Allocate storage for CPU status register           */
    OS_CPU_SR  cpu_sr;
#endif
    OS_Q      *pq;


#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {                    /* Validate 'pevent'                             */
        return (OS_ERR_PEVENT_NULL);
    }
    if (msg == (void *)0) {                           /* Make sure we are not posting a NULL pointer   */
        return (OS_ERR_POST_NULL_PTR);
    }
    if (pevent->OSEventType != OS_EVENT_TYPE_Q) {     /* Validate event block type                     */
        return (OS_ERR_EVENT_TYPE);
    }
#endif
    OS_ENTER_CRITICAL();
//如果事件控制块的 任务等待表 有等待任务
    if (pevent->OSEventGrp != 0x00) {                 /* See if any task pending on queue              */
        //判断将 消息发给 优先级最高的任务,还是将消息发送给所有任务
        if ((opt & OS_POST_OPT_BROADCAST) != 0x00) {  /* Do we need to post msg to ALL waiting tasks ? */
            //轮询 将有消息发送给所有等待任务
            //所有等待任务加入就绪表
            while (pevent->OSEventGrp != 0x00) {      /* Yes, Post to ALL tasks waiting on queue       */
                OS_EventTaskRdy(pevent, msg, OS_STAT_Q);
            }
        } else {
             //将有消息发送给优先级最高的任务
            OS_EventTaskRdy(pevent, msg, OS_STAT_Q);  /* No,  Post to HPT waiting on queue             */
        }
        OS_EXIT_CRITICAL();
        //系统调度一次
        OS_Sched();                                   /* Find highest priority task ready to run       */
        return (OS_NO_ERR);
    }
//如果事件控制块的 任务等待表 无等待任务
    pq = (OS_Q *)pevent->OSEventPtr;                  /* Point to queue control block                  */
    //确保队列非空
    if (pq->OSQEntries >= pq->OSQSize) {              /* Make sure queue is not full                   */
        OS_EXIT_CRITICAL();
        return (OS_Q_FULL);
    }
    //消息 后入先出
    if ((opt & OS_POST_OPT_FRONT) != 0x00) {          /* Do we post to the FRONT of the queue?         */
        //在消息入队前 判是否是第一个消息
        if (pq->OSQOut == pq->OSQStart) {             /* Yes, Post as LIFO, Wrap OUT pointer if we ... */
            pq->OSQOut = pq->OSQEnd;                  /*      ... are at the 1st queue entry           */
        }
        pq->OSQOut--;
        *pq->OSQOut = msg;                            /*      Insert message into queue                */
    }
    //消息 后入后出
     else {                                          /* No,  Post as FIFO                             */
        *pq->OSQIn++ = msg;                           /*      Insert message into queue                */
        //在消息入队后 判是否是最后一个消息
        if (pq->OSQIn == pq->OSQEnd) {                /*      Wrap IN ptr if we are at end of queue    */
            pq->OSQIn = pq->OSQStart;
        }
    }
    //队列中消息数目加1
    pq->OSQEntries++;                                 /* Update the nbr of entries in the queue        */
    OS_EXIT_CRITICAL();
    return (OS_NO_ERR);
}

队列创建后的结构
在消息进行获取释放中,消息在队列的来源
请添加图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值