消息队列

在前一篇文章中【TencentOS tiny学习】源码分析(3)——队列

我们描述了TencentOS tiny的队列实现,同时也点出了TencentOS tiny的队列是依赖于消息队列的,那么我们今天来看看消息队列的实现。

其实消息队列是TencentOS tiny的一个基础组件,作为队列的底层。

所以在tos_config.h中会用以下宏定义:

#if (TOS_CFG_QUEUE_EN > 0u)
#define TOS_CFG_MSG_EN 1u
#else
#define TOS_CFG_MSG_EN 0u
#endif

系统消息池初始化

在系统初始化(tos_knl_init())的时候,系统就会将消息池进行初始化,其中, msgpool_init()函数就是用来初始化消息池的,该函数的定义位于 tos_msg.c文件中,函数的实现主要是通过一个for循环,将消息池k_msg_pool[TOS_CFG_MSG_POOL_SIZE]的成员变量进行初始化,初始化对应的列表节点,并且将它挂载到空闲消息列表上k_msg_freelist

初始化完成示意图:(假设只有3个消息)

__KERNEL__ void msgpool_init(void)
{
uint32_t i; for (i = 0; i < TOS_CFG_MSG_POOL_SIZE; ++i) {
tos_list_init(&k_msg_pool[i].list);
tos_list_add(&k_msg_pool[i].list, &k_msg_freelist);
}
}
__API__ k_err_t tos_knl_init(void)
{
···
#if (TOS_CFG_MSG_EN) > 0
msgpool_init();
#endif
···
}

消息队列创建

这个函数在队列创建中会被调用,当然他也可以自己作为用户API接口提供给用户使用,而非仅仅是内核API接口。

这个函数的本质上就是初始化消息队列中的消息列表queue_head

初始化完成示意图:

__API__ k_err_t tos_msg_queue_create(k_msg_queue_t *msg_queue)
{
TOS_PTR_SANITY_CHECK(msg_queue); #if TOS_CFG_OBJECT_VERIFY_EN > 0u
knl_object_init(&msg_queue->knl_obj, KNL_OBJ_TYPE_MSG_QUEUE);
#endif tos_list_init(&msg_queue->queue_head);
return K_ERR_NONE;
}

消息队列销毁

tos_msg_queue_destroy()函数用于销毁一个消息队列,当消息队列不在使用是可以将其销毁,销毁的本质其实是将消息队列控制块的内容进行清除,首先判断一下消息队列控制块的类型是KNL_OBJ_TYPE_MSG_QUEUE,这个函数只能销毁队列类型的控制块。然后调用tos_msg_queue_flush()函数将队列的消息列表的消息全部“清空”,“清空”的意思是将挂载到队列上的消息释放回消息池(如果消息队列的消息列表存在消息,使用msgpool_free()函数释放消息)。并且通过tos_list_init()函数将消息队列的消息列表进行初始化,knl_object_deinit()函数是为了确保消息队列已经被销毁,此时消息队列控制块的pend_obj成员变量中的type 属性标识为KNL_OBJ_TYPE_NONE

但是有一点要注意,因为队列控制块的RAM是由编译器静态分配的,所以即使是销毁了队列,这个内存也是没办法释放的~

__API__ k_err_t tos_msg_queue_destroy(k_msg_queue_t *msg_queue)
{
TOS_PTR_SANITY_CHECK(msg_queue); #if TOS_CFG_OBJECT_VERIFY_EN > 0u
if (!knl_object_verify(&msg_queue->knl_obj, KNL_OBJ_TYPE_MSG_QUEUE)) {
return K_ERR_OBJ_INVALID;
}
#endif tos_msg_queue_flush(msg_queue);
tos_list_init(&msg_queue->queue_head); #if TOS_CFG_OBJECT_VERIFY_EN > 0u
knl_object_deinit(&msg_queue->knl_obj);
#endif return K_ERR_NONE;
}
__API__ void tos_msg_queue_flush(k_msg_queue_t *msg_queue)
{
TOS_CPU_CPSR_ALLOC();
k_list_t *curr, *next; TOS_CPU_INT_DISABLE(); TOS_LIST_FOR_EACH_SAFE(curr, next, &msg_queue->queue_head) {
msgpool_free(TOS_LIST_ENTRY(curr, k_msg_t, list));
} TOS_CPU_INT_ENABLE();
}

从消息队列获取消息

tos_msg_queue_get()函数用于从消息队列中获取消息,获取到的消息通过msg_addr参数返回,获取到消息的大小通过msg_size参数返回给用户,当获取成功是返回K_ERR_NONE,否则返回对应的错误代码。

这个从消息队列中获取消息的函数是不会产生阻塞的,如果有消息则获取成功,否则就获取失败,它的实现过程如下:

TOS_CFG_OBJECT_VERIFY_EN 宏定义使能了,就调用knl_object_verify()函数确保是从消息队列中获取消息,然后通过TOS_LIST_FIRST_ENTRY_OR_NULL判断一下是消息队列的消息列表否存在消息,如果不存在则返回K_ERR_MSG_QUEUE_EMPTY表示消息队列是空的,反正将获取成功,获取成功后需要使用msgpool_free()函数将消息释放回消息池。

__API__ k_err_t tos_msg_queue_get(k_msg_queue_t *msg_queue, void **msg_addr, size_t *msg_size)
{
TOS_CPU_CPSR_ALLOC();
k_msg_t *msg; #if TOS_CFG_OBJECT_VERIFY_EN > 0u
if (!knl_object_verify(&msg_queue->knl_obj, KNL_OBJ_TYPE_MSG_QUEUE)) {
return K_ERR_OBJ_INVALID;
}
#endif TOS_CPU_INT_DISABLE(); msg = TOS_LIST_FIRST_ENTRY_OR_NULL(&msg_queue->queue_head, k_msg_t, list);
if (!msg) {
TOS_CPU_INT_ENABLE();
return K_ERR_MSG_QUEUE_EMPTY;
} *msg_addr = msg->msg_addr;
*msg_size = msg->msg_size;
msgpool_free(msg); TOS_CPU_INT_ENABLE(); return K_ERR_NONE;
}

向消息队列写入消息

当发送消息时,TencentOS tiny会从消息池(空闲消息列表)中取出一个空闲消息,挂载到消息队列的消息列表中,可以通过opt参数选择挂载到消息列表的末尾或者是头部,因此消息队列的写入是支持FIFOLIFO方式的,msg_queue是要写入消息的消息队列控制块,msg_addrmsg_size则是要写入消息的地址与大小。

写入消息的过程非常简单,直接通过msgpool_alloc()函数从消息池取出一个空闲消息,如果系统不存在空闲的消息,则直接返回错误代码K_ERR_MSG_QUEUE_FULL表示系统可用的消息已经被使用完。如果取出空闲消息成功则将要写入的消息地址与大小记录到消息池的msg_addrmsg_size 成员变量中,然后通过opt参数选择将消息挂载到消息列表的位置(头部或者是尾部)。

__API__ k_err_t tos_msg_queue_put(k_msg_queue_t *msg_queue, void *msg_addr, size_t msg_size, k_opt_t opt)
{
TOS_CPU_CPSR_ALLOC();
k_msg_t *msg; #if TOS_CFG_OBJECT_VERIFY_EN > 0u
if (!knl_object_verify(&msg_queue->knl_obj, KNL_OBJ_TYPE_MSG_QUEUE)) {
return K_ERR_OBJ_INVALID;
}
#endif TOS_CPU_INT_DISABLE(); msg = msgpool_alloc();
if (!msg) {
TOS_CPU_INT_ENABLE();
return K_ERR_MSG_QUEUE_FULL;
} msg->msg_addr = msg_addr;
msg->msg_size = msg_size; if (opt & TOS_OPT_MSG_PUT_LIFO) {
tos_list_add(&msg->list, &msg_queue->queue_head);
} else {
tos_list_add_tail(&msg->list, &msg_queue->queue_head);
} TOS_CPU_INT_ENABLE(); return K_ERR_NONE;
}

实验测试代码

#include "stm32f10x.h"
#include "bsp_usart.h"
#include "tos.h" k_msg_queue_t test_msg_queue_00; k_task_t task1;
k_task_t task2;
k_stack_t task_stack1[1024];
k_stack_t task_stack2[1024]; void test_task1(void *Parameter)
{
k_err_t err;
int i = 0;
int msg_received;
size_t msg_size = 0; while(1)
{
printf("queue pend\r\n");
for (i = 0; i < 3; ++i)
{
err = tos_msg_queue_get(&test_msg_queue_00, (void **)&msg_received, &msg_size);
if (err == K_ERR_NONE)
printf("msg queue get is %d \r\n",msg_received); if (err == K_ERR_PEND_DESTROY)
{
printf("queue is destroy\r\n");
tos_task_delay(TOS_TIME_FOREVER - 1);
}
}
tos_task_delay(1000);
}
} void test_task2(void *Parameter)
{
k_err_t err; int i = 0;
uint32_t msgs[3] = { 1, 2, 3 }; printf("task2 running\r\n"); while(1)
{
for (i = 0; i < 3; ++i)
{
err = tos_msg_queue_put(&test_msg_queue_00, (void *)(msgs[i]), sizeof(uint32_t), TOS_OPT_MSG_PUT_FIFO);
if (err != K_ERR_NONE)
printf("msg queue put fail! code : %d \r\n",err);
} tos_task_delay(1000);
}
}
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
k_err_t err; /*初始化USART 配置模式为 115200 8-N-1,中断接收*/
USART_Config(); printf("Welcome to TencentOS tiny\r\n"); tos_knl_init(); // TOS Tiny kernel initialize tos_robin_config(TOS_ROBIN_STATE_ENABLED, (k_timeslice_t)500u); printf("create test_queue_00\r\n");
err = tos_msg_queue_create(&test_msg_queue_00);
if(err != K_ERR_NONE)
printf("TencentOS Create test_msg_queue_00 fail! code : %d \r\n",err); printf("create task1\r\n");
err = tos_task_create(&task1,
"task1",
test_task1,
NULL,
3,
task_stack1,
1024,
20);
if(err != K_ERR_NONE)
printf("TencentOS Create task1 fail! code : %d \r\n",err); printf("create task2\r\n");
err = tos_task_create(&task2,
"task2",
test_task2,
NULL,
4,
task_stack2,
1024,
20);
if(err != K_ERR_NONE)
printf("TencentOS Create task2 fail! code : %d \r\n",err); tos_knl_start(); // Start TOS Tiny }

现象

喜欢就关注我吧!

相关代码可以在公众号后台回复 “ 19 ” 获取。

更多资料欢迎关注“物联网IoT开发”公众号!

最新文章

  1. python基础——匿名函数
  2. 转:Java实现几种常见排序方法
  3. getHibernateTemplate() 一直报NullPointerException 错误
  4. Flex通信-与Java实现Socket通信实例
  5. onInterceptTouchEvent和onTouchEvent举例分析
  6. PL/SQL Developer编码格式设置及中文乱码解决方案
  7. win7下搭建nginx+php的开发环境
  8. ParseChat应用源码ios版
  9. JavaScript中的call 和apply的用途以及区别
  10. 【Netty】(8)---理解ChannelPipeline
  11. 【oracle】dmp导数据库
  12. SpringMVC项目容易出现的BUG
  13. 调用Android自带浏览器打开网页
  14. centos7下安装docker(15.5容器跨主机网络--flanneld)
  15. 吴恩达课后作业学习1-week2-homework-logistic
  16. centos7下stf安装介绍(一)----环境搭建
  17. php 腾讯云 对象存储V5版本 获取返回的上传文件的链接方法
  18. 学习mysql replication gitd
  19. Greenplum入门——基础知识、安装、常用函数
  20. ios core plot设置xy坐标

热门文章

  1. 「每日五分钟,玩转JVM」:对象内存布局
  2. nvm 管理多个活动的node.js版本
  3. 【Offer】[37] 【序列化二叉树】
  4. 【Offer】[36] 【二叉搜索树与双向链表】
  5. 深入理解SQL Server数据库Select查询原理(一)
  6. 微软发布.Net Core 3.0 RC1,最终版本定于9月23日
  7. 二分练习题3 查找小于x的最大元素 题解
  8. win下的mongodb安装和基础操作
  9. DirectX12 3D 游戏开发与实战第四章内容(上)
  10. 网络编程之socket模块