设为首页 加入收藏

TOP

uC/OSIII的消息队列处理机制(三)
2015-11-21 02:13:24 来源: 作者: 【 】 浏览:51
Tags:uC/OSIII 消息 队列 处理 机制
_PEND_DATA时有介绍,可以回头去再看一下。写入代码如下:
p_pend_data->RdyObjPtr = p_obj;
p_pend_data->RdyMsgPtr = p_void;
p_pend_data->RdyMsgSize = msg_size;
p_pend_data->RdyTS = ts;
接下来由MultiPend的任务自己判断就绪的是队列还是信号量,然后提取出相应的消息内容指针,这个是任务处理中自己的家事,由写应用的程序员到时操心,这里就不再关心了。
消息推送过程到此结束。
这里还要再增加一些内容,就是uC/OSIII里有任务消息队列,这个消息队列与OS_Q的区别就是:不需要定义OS_Q,因为它是在任务TCB定义时,被直接定义在任务TCB里面了!伴随任务一生。uC/OSIII真的很舍得,看一下它的定义:
struct os_tcb {
......
#if OS_CFG_TASK_Q_EN > 0u
OS_MSG_Q MsgQ;
......
}
可见,在任务TCB中定义的是OS_MSG_Q,而不是OS_Q,为什么呢?前面说过OS_Q中包含两个重要的主线,这里再把它们列写如下:
OS_Q->OS_PEND_LIST<->OS_PEND_DATA <-> 任务TCB
OS_Q->OS_MSG_Q ->OS_MSG -> void *MsgPtr和MsgSize->宝藏
可见,任务TCB与宝藏相连的纽带就是OS_Q,那既然任务TCB自己都可以包含消息队列了,还要OS_Q干啥,是不是。前面又说过,OS_MSG_Q就是消息队列OS_Q的管家,所以任务TCB中直接定义OS_MSG_Q,找到宝藏就得了呗。
任务队列推送函数叫OSTaskQPost(),里面调用的是OS_TaskQPost(),该函数是被定义在ucosiii\source文件夹下的os_task.c中的,它与普通OS_QPost()函数是同样的过程,里面也是调用OS_MsgQPut()进行无任务等待时的推送,调用OS_Post()进行本任务等待时的推送。唯一不同的是,它的输入参数中不是*OS_Q类型,而*TCB,省去了通过队列再查找TCB的过程,所以它的推送是非常快的,是直接推送。这也是uC/OSIII建议使用的消息队列推送方式。
个人认为,uC/OSIII不惜浪费TCB空间打造任务信号量,任务队列,目的就是要减少使用普通信号量和普通队列,因为进程间通信通常都是点对点的,这将大幅度提高效率。而普通信号量和普通队列存在的唯一目的,就是多任务Post和MultiPend这两种特殊情况,而uC/OSIII又指出,这两种特殊情况都是可能会长时间关中断的,建议少用。
消息队列推送机制基本就这些了,还剩下点边边角角的不值得再继续深入。


下面就是另一个重要方向,消息等待。uC/OSIII中的消息等待又分为三部分:普通消息队列等待函数void *OSQPend();任务消息队列等待函数void *OSTaskQPend();多对象等待函数OS_OBJ_QTY OSMultiPend()。
这里重点看第一个,任务调用void *OSQPend()后即进入等待消息状态。
OSQPend()函数是一个比较长的函数(通常接收器都比发送器要复杂一点),但简单讲,它可以分为两大部分:
一、准备进入任务挂起状态,将TCB写入到对应的要等待的消息队列下面的任务挂起表中;
然后执行调试,当前任务阻塞,其他任务执行;
二、收到消息后,从pend状态返回来,继续执行,把收到的消息指针取出来。
注意这两大部分的执行通常都是时间上分开的,但在空间上却是在一起的,就是代码被写在同一个函数里,这也正是Pend()函数的特点。下面分开介绍:
状态一,准备进入任务挂起状态,将TCB写入到对应的要等待的消息队列下面的任务挂起表中 。在这个过程中,Pend()函数做了几下几方面工作:先检查要pend的消息队列中是否已经有之前被post过来的消息存储在里面,如果有,就省事了,直接返回,不pend;另外,如果在输入参数中指定了不pend,或者是在中断中执行的,都不能pend,必须立即返回;如果没有之前的消息被存储,也没有在中断中,也指定了要pend,则准备进入阻塞等待状态,将挂起表等数据结构都准备好,将TCB写入其中。
二、收到消息后,从pend状态返回来,继续执行,如果是正常post过来的消息,就把收到的消息指针取出来,这是正常返回的情况。也有可能是等待超时,或者是消息队列被删除了,或者是pend被人为的abort了,这些异常情况都要进行判断拦截,然后返回空指针,并返回一个错误。
OSQPend()函数的处理过程就是这样的,具体函数内容如下:
void *OSQPend (OS_Q *p_q,
OS_TICK timeout,
OS_OPT opt,
OS_MSG_SIZE *p_msg_size,
CPU_TS *p_ts,
OS_ERR *p_err)
{
OS_PEND_DATA pend_data;
void *p_void;
CPU_SR_ALLOC();
/*参数检查代码略*/
CPU_CRITICAL_ENTER();
p_void = OS_MsgQGet(&p_q->MsgQ, /* 判断队列里是否有已经被推送过的消息*/
p_msg_size,
p_ts,
p_err);
if (*p_err == OS_ERR_NONE) {
CPU_CRITICAL_EXIT();
return (p_void); /* 如果队列里有消息存在,直接返回,不pend */
}

if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {
/*如果是在中断中,不能pend,必须立即返回*/
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_SCHED_LOCKED;
return ((void *)0);
}

OS_CRITICAL_ENTER_CPU_EXIT();
/*锁定调度器*/
OS_Pend(&pend_data, /* 准备进入阻塞等待状态,将挂起表等数据结构都准备好*/
(OS_PEND_OBJ *)((void *)p_q),
OS_TASK_PEND_ON_Q,
timeout);
OS_CRITICAL_EXIT_NO_SCHED();
/*退出调度锁定,并且不调度*/

OSSched(); /*进入调度点,切换到其他任务,到此,本任务处于暂停状态,在等待到消息
到达之前,不会再执行以下代码 */

/* 以下为从别的任务切换回来继续执行的代码,可能为pend获得,也可能为超时、删除了,
pend获得的内容是被保留在本任务TCB的MsgPtr和MsgSize中 */
CPU_CRITICAL_ENTER();
switch (OSTCBCurPtr->PendStatus) {
case OS_STATUS_PEND_OK: /* 是正常推送过来的消息 */
/*从本任务的TCB中MsgPtr和MsgSize中取出消息*/
p_void = OSTCBCurPtr->MsgPtr;

*p_msg_size = OSTCBCurPtr->MsgSize;
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
*p_err = OS_ERR_NONE;
break;

case OS_STATUS_PEND_ABORT: /* 如果是消息队列被abort的,

首页 上一页 1 2 3 4 下一页 尾页 3/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇POJ 1265 多边形格点数Pick公式 下一篇2013级C++第4周(春)项目――再和..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: