当前位置:网站首页>【Ucos-III源码分析】——消息队列
【Ucos-III源码分析】——消息队列
2022-07-16 14:05:00 【你的先生_1016】
一、消息队列的创建
void OSQCreate (OS_Q *p_q,
CPU_CHAR *p_name,
OS_MSG_QTY max_qty,
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_q == (OS_Q *)0) { /* Validate arguments */
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
if (max_qty == (OS_MSG_QTY)0) { /* Cannot specify a zero size queue */
*p_err = OS_ERR_Q_SIZE;
return;
}
#endif
OS_CRITICAL_ENTER();
p_q->Type = OS_OBJ_TYPE_Q; /* Mark the data structure as a message queue */
p_q->NamePtr = p_name;
OS_MsgQInit(&p_q->MsgQ, /* Initialize the queue */
max_qty);
OS_PendListInit(&p_q->PendList); /* Initialize the waiting list */
#if OS_CFG_DBG_EN > 0u
OS_QDbgListAdd(p_q);
#endif
OSQQty++; /* One more queue created */
OS_CRITICAL_EXIT();
*p_err = OS_ERR_NONE;
}从这段代码可以看出,消息队列的创建比较简单,基本分为以下几个步骤
1、检查参数,设置obj的类型为消息队列类型,赋值名字
2、消息队列初始化,这里的结构可以配合图来看。
void OS_MsgQInit (OS_MSG_Q *p_msg_q,
OS_MSG_QTY size)
{
p_msg_q->NbrEntriesSize = (OS_MSG_QTY)size;
p_msg_q->NbrEntries = (OS_MSG_QTY)0;
p_msg_q->NbrEntriesMax = (OS_MSG_QTY)0;
p_msg_q->InPtr = (OS_MSG *)0;
p_msg_q->OutPtr = (OS_MSG *)0;
}
这里就是消息队列的结构类型,这里的inptr和outptr,是因为本身队列结构,可以设置进来的消息体是先进先出还是先进后出。
3、清空消息队列的等待列表
void OS_PendListInit (OS_PEND_LIST *p_pend_list)
{
p_pend_list->HeadPtr = (OS_PEND_DATA *)0;
p_pend_list->TailPtr = (OS_PEND_DATA *)0;
p_pend_list->NbrEntries = (OS_OBJ_QTY )0;
}
4、计数全局的消息队列变量 OSQQty++;
这样就完成了一个消息队列的创建。可以看到非常的简单。
这里还需要注意,创建消息队列的时候还会并带着创建一个消息池,在没有消息的时候消息池为空,虽然说是空消息,但是确实存在而且个数是由系统定义好的一个最大值。也是一个单链表的形式。

这里面还有一个当有消息进入到消息队列的时候这时候消息就不会放在消息池,而是放在消息队列下面的两个指针管理的单项链表中,来管理是先入先出还是先入后出

可以看到这里因为是队列需要看是设置的是先进先出还是先进后出,所以有进入和出去两个指针。
二、挂起等待消息队列
1、这里的话就是普通的对于,除开了简单的参数检查之后,这里还会对于你传递的opt参数进行一个判断,主要判断就是你的是阻塞等待还是非阻塞等待。阻塞就继续往下,非阻塞就退出
OS_PEND_DATA pend_data;
void *p_void;
CPU_SR_ALLOC();
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return ((void *)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 ((void *)0);
}
#endif
#if OS_CFG_ARG_CHK_EN > 0u
if (p_q == (OS_Q *)0) { /* Validate arguments */
*p_err = OS_ERR_OBJ_PTR_NULL;
return ((void *)0);
}
if (p_msg_size == (OS_MSG_SIZE *)0) {
*p_err = OS_ERR_PTR_INVALID;
return ((void *)0);
}
switch (opt) {
case OS_OPT_PEND_BLOCKING:
case OS_OPT_PEND_NON_BLOCKING:
break;
default:
*p_err = OS_ERR_OPT_INVALID;
return ((void *)0);
}2、判断这个消息队列中有没有信息,这里用的就是一个判断这个消息队列里面一共存了多少个,如果是0代表里面没有内容,意思就返回空,相当于你想问消息队列有东西吗,但是里面就是空的在这里这是没有的情况
如果你想拿消息队列,并且里面也有。那么就要看里面到底又多少个,如果就一个(因为所有 需要从消息队列里面输出的消息体,都被两个指针管理着,就是inptr,outptr这两个)如果你需要取那么他就从outptr里面给你取出一个来,然后outptr又指向下一个,此时就要判断你是不是取完了,如果取完了就把这个消息队列置空,存的个数也置空,如果还有就数量--,最后返回消息体的指针。
void *OS_MsgQGet (OS_MSG_Q *p_msg_q,
OS_MSG_SIZE *p_msg_size,
CPU_TS *p_ts,
OS_ERR *p_err)
{
OS_MSG *p_msg;
void *p_void;
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return ((void *)0);
}
#endif
if (p_msg_q->NbrEntries == (OS_MSG_QTY)0) {
*p_msg_size = (OS_MSG_SIZE)0;
if (p_ts != (CPU_TS *)0) {
*p_ts = (CPU_TS )0;
}
*p_err = OS_ERR_Q_EMPTY;
return ((void *)0);
}
p_msg = p_msg_q->OutPtr;
p_void = p_msg->MsgPtr;
*p_msg_size = p_msg->MsgSize;
if (p_ts != (CPU_TS *)0) {
*p_ts = p_msg->MsgTS;
}
p_msg_q->OutPtr = p_msg->NextPtr;
if (p_msg_q->OutPtr == (OS_MSG *)0) {
p_msg_q->InPtr = (OS_MSG *)0;
p_msg_q->NbrEntries = (OS_MSG_QTY)0;
} else {
p_msg_q->NbrEntries--;
}
p_msg->NextPtr = OSMsgPool.NextPtr; /* Return message control block to free list */
OSMsgPool.NextPtr = p_msg;
OSMsgPool.NbrFree++;
OSMsgPool.NbrUsed--;
*p_err = OS_ERR_NONE;
return (p_void);
}3、这里如果是非阻塞等待,就直接返回给你,没有
if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { /* Caller wants to block if not available? */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_PEND_WOULD_BLOCK; /* No */
return ((void *)0);
} else {
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Can't pend when the scheduler is locked */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_SCHED_LOCKED;
return ((void *)0);
}
}4、在后续就是一个如果消息队列没东西,那么你任务又是阻塞等待,就需要把任务挂载到等待列表,这里就是先设置你任务的一个状态,然后判断这个任务是不是需要超时等待选项。如果需要就把任务添加到一个时钟基准列表里面去,这里是传递进去之后判断。,然后就是很简单的初始化需要挂载的pend_data,然后把任务根据优先级去插入到等待列表中。
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 ... */
p_pend_list = &p_obj->PendList; /* ... if there is an object to pend on */
p_pend_data->PendObjPtr = p_obj; /* Save the pointer to the object pending on */
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
}5、挂载完了之后就是堆任务状态的一个判断,
- 挂载ok:存了一个时间戳,好像是把拿到消息和消息的大小了,然后返回了
- 取消等待:等待超时:删除:这里就是终止了,不等了。啥也没有返回空(三个情况基本一样)
这样基本上就结束了
switch (OSTCBCurPtr->PendStatus) {
case OS_STATUS_PEND_OK: /* Extract message from TCB (Put there by Post) */
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: /* Indicate that we aborted */
p_void = (void *)0;
*p_msg_size = (OS_MSG_SIZE)0;
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 event within TO */
p_void = (void *)0;
*p_msg_size = (OS_MSG_SIZE)0;
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 */
p_void = (void *)0;
*p_msg_size = (OS_MSG_SIZE)0;
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
*p_err = OS_ERR_OBJ_DEL;
break;
default:
p_void = (void *)0;
*p_msg_size = (OS_MSG_SIZE)0;
*p_err = OS_ERR_STATUS_INVALID;
break;
}
CPU_CRITICAL_EXIT();
return (p_void);三、发布消息队列的内容
void OSQPost (OS_Q *p_q,
void *p_void,
OS_MSG_SIZE msg_size,
OS_OPT opt,
OS_ERR *p_err)
{
CPU_TS ts;
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#if OS_CFG_ARG_CHK_EN > 0u
if (p_q == (OS_Q *)0) { /* Validate arguments */
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
#endif
#if OS_CFG_OBJ_TYPE_CHK_EN > 0u
if (p_q->Type != OS_OBJ_TYPE_Q) { /* Make sure message queue was created */
*p_err = OS_ERR_OBJ_TYPE;
return;
}
#endif
ts = OS_TS_GET(); /* Get timestamp */
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u
if (OSIntNestingCtr > (OS_NESTING_CTR)0) {
OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_Q, /* Post to ISR queue */
(void *)p_q,
(void *)p_void,
(OS_MSG_SIZE)msg_size,
(OS_FLAGS )0,
(OS_OPT )opt,
(CPU_TS )ts,
(OS_ERR *)p_err);
return;
}
#endif
OS_QPost(p_q,
p_void,
msg_size,
opt,
ts,
p_err);
}所以说这个发布函数重要的就是在最后的这一个函数里面,上面哪个是在中断中去发布消息,原理和下面这个类似,都是发布消息到消息队列中
void OS_QPost (OS_Q *p_q,
void *p_void,
OS_MSG_SIZE msg_size,
OS_OPT opt,
CPU_TS ts,
OS_ERR *p_err)
{
OS_OBJ_QTY cnt;
OS_OPT post_type;
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();
OS_CRITICAL_ENTER();
p_pend_list = &p_q->PendList;
if (p_pend_list->NbrEntries == (OS_OBJ_QTY)0) { /* Any task waiting on message queue? */
if ((opt & OS_OPT_POST_LIFO) == (OS_OPT)0) { /* Determine whether we post FIFO or LIFO */
post_type = OS_OPT_POST_FIFO;
} else {
post_type = OS_OPT_POST_LIFO;
}
OS_MsgQPut(&p_q->MsgQ, /* Place message in the message queue */
p_void,
msg_size,
post_type,
ts,
p_err);
OS_CRITICAL_EXIT();
return;
}
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 */
}
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_q),
p_tcb,
p_void,
msg_size,
ts);
p_pend_data = p_pend_data_next;
cnt--;
}
OS_CRITICAL_EXIT_NO_SCHED();
if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) {
OSSched(); /* Run the scheduler */
}
*p_err = OS_ERR_NONE;
}首先要确认的就是先入先出还是先入后出。这里通过第一个if来判断出来,再次进入一个函数OS_MsgQPut这个函数。
void OS_MsgQPut (OS_MSG_Q *p_msg_q,
void *p_void,
OS_MSG_SIZE msg_size,
OS_OPT opt,
CPU_TS ts,
OS_ERR *p_err)
{
OS_MSG *p_msg;
OS_MSG *p_msg_in;
#ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
if (p_msg_q->NbrEntries >= p_msg_q->NbrEntriesSize) {
*p_err = OS_ERR_Q_MAX; /* Message queue cannot accept any more messages */
return;
}
if (OSMsgPool.NbrFree == (OS_MSG_QTY)0) {
*p_err = OS_ERR_MSG_POOL_EMPTY; /* No more OS_MSG to use */
return;
}
p_msg = OSMsgPool.NextPtr; /* Remove message control block from free list */
OSMsgPool.NextPtr = p_msg->NextPtr;
OSMsgPool.NbrFree--;
OSMsgPool.NbrUsed++;
if (p_msg_q->NbrEntries == (OS_MSG_QTY)0) { /* Is this first message placed in the queue? */
p_msg_q->InPtr = p_msg; /* Yes */
p_msg_q->OutPtr = p_msg;
p_msg_q->NbrEntries = (OS_MSG_QTY)1;
} else {
if ((opt & OS_OPT_POST_LIFO) == OS_OPT_POST_FIFO) { /* Assume FIFO if not LIFO */
p_msg_in = p_msg_q->InPtr; /* FIFO */
p_msg_in->NextPtr = p_msg;
p_msg->NextPtr = (OS_MSG *)0;
p_msg_q->InPtr = p_msg;
} else {
p_msg->NextPtr = p_msg_q->OutPtr; /* LIFO */
p_msg_q->OutPtr = p_msg;
}
p_msg_q->NbrEntries++;
}
if (p_msg_q->NbrEntries > p_msg_q->NbrEntriesMax) {
p_msg_q->NbrEntriesMax = p_msg_q->NbrEntries;
}
p_msg->MsgPtr = p_void; /* Deposit message in the message queue entry */
p_msg->MsgSize = msg_size;
p_msg->MsgTS = ts;
*p_err = OS_ERR_NONE;
}这里进来就是看你消息队列里面存满了没,存满了自然就不让存了。再就是判断你的消息池是不是满了,这里面放消息就是先从消息池拿一个空消息出来,再把你的消息内容挂接到这个空消息上,再把这个消息体挂载到消息队列里面去。这里面插入消息就要看你消息队列里面有没有消息,有和没有是两种不同的办法,有的话还要看你是FIFO还是LIFO原理都差不多,就是插入的位置不一样,再把队列的消息数量++。
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 */
}
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_q),
p_tcb,
p_void,
msg_size,
ts);
p_pend_data = p_pend_data_next;
cnt--;
}
OS_CRITICAL_EXIT_NO_SCHED();
if ((opt & OS_OPT_POST_NO_SCHED) == (OS_OPT)0) {
OSSched(); /* Run the scheduler */
}
*p_err = OS_ERR_NONE;在下面这种情况就是等待队列中有任务在等待,意思你的消息是发布给任务的,不是发布给消息队列。这一段里面就是把消息直接給在等待的任务,这大概意思就是获取有多少个任务在等待消息队列,然后把这些等待消息队列的任务从等待列表中移除(利用循环),并且把消息内容给他们呢去返回。
边栏推荐
- Pay equal attention to quality and efficiency, test left shift power block storage technology research and development
- C#网络应用编程,实验七: 异步编程练习
- Basic operation of queue
- 移掉K位数字[贪心思想 & 单调栈实现]
- 监听拖拽事件,第一次拖拽得不到上传的文件内容,第二次以后就能正常得到上传的文件内容
- 北京华联BHG Mall持续发力,BHG DAY引领城市消费新热潮
- WordPress personal blog theme wp-reason-v1.0
- Log4j log configuration
- 关于STM32驱动LCD显示屏,程序下载后白屏、乱码需要上电复位才能恢复正常问题的解决办法
- rman异机恢复后,报错ora-01017
猜你喜欢

WordPress个人博客主题wp-Concise-v1.0

WordPress personal blog theme wp-reason-v1.0

北京华联BHG Mall持续发力,BHG DAY引领城市消费新热潮
![Leetcode refers to the average value of offer II 041 sliding window [sliding window] the way of leetcode in heroding](/img/29/ea4b91cc90ca0dd0eea7e6a3d33394.png)
Leetcode refers to the average value of offer II 041 sliding window [sliding window] the way of leetcode in heroding

京东金融,你到底是坏,还是码农裁多了??

HALCON联合C#检测表面缺陷——ROI交互(三)(和图片同步缩放裁剪等功能)

Leetcode 49. 字母异位词分组

Leetcode45. 跳跃游戏 II
![[基础服务] [数据库] MySQL 主从复制部署与配置](/img/6d/0ad81ae080153fc2e50dc60c39e1bd.png)
[基础服务] [数据库] MySQL 主从复制部署与配置

2022 hoisting machinery command examination simulation 100 questions simulation examination platform operation
随机推荐
网络爬虫爬取三国演义所有章节的标题和内容(BeautifulSoup解析)
Package C language files into exe executable programs
京东金融,你到底是坏,还是码农裁多了??
QT4: develop "work diary" from scratch (1) -worklog project
The art of code annotation. Does excellent code really need no annotation?
Common network devices and network reference models, as well as common network layer protocols and data communication processes
100% accuracy, Alibaba business travel billing system architecture design practice
【golang】cannot unmarshal xxx “ into Go struct field xxx of type xxx
Log4j log configuration
2022.7.15-----leetcode.558
Xray安装使用
Boost create folder
ctf-pikachu-RCE
质量与效率并重,测试左移助力块存储技术研发
C#网络应用编程,实验一:WPF练习
深圳某游戏研发公司给每个工位都装监控,网友:堪比坐牢!
10 first and 2 second, the NLP team of Dharma hall won the championship in semeval 2022
C # basic practice of network application programming and asynchronous programming
2022 simulated 100 questions and simulated examination for the main principals of hazardous chemical business units
Data visualization: plotting pie chart with Matplotlib