消息队列

在前一篇文章中【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开发”公众号!

【TencentOS tiny】深度源码分析(4)——消息队列的更多相关文章

  1. Spring5深度源码分析(三)之AnnotationConfigApplicationContext启动原理分析

    代码地址:https://github.com/showkawa/spring-annotation/tree/master/src/main/java/com/brian AnnotationCon ...

  2. 源码分析RocketMQ消息轨迹

    目录 1.发送消息轨迹流程 1.1 DefaultMQProducer构造函数 1.2 SendMessageTraceHookImpl钩子函数 1.3 TraceDispatcher实现原理 2. ...

  3. 源码分析 Kafka 消息发送流程(文末附流程图)

    温馨提示:本文基于 Kafka 2.2.1 版本.本文主要是以源码的手段一步一步探究消息发送流程,如果对源码不感兴趣,可以直接跳到文末查看消息发送流程图与消息发送本地缓存存储结构. 从上文 初识 Ka ...

  4. 源码分析Kafka 消息拉取流程

    目录 1.KafkaConsumer poll 详解 2.Fetcher 类详解 本节重点讨论 Kafka 的消息拉起流程. @(本节目录) 1.KafkaConsumer poll 详解 消息拉起主 ...

  5. jQuery源码分析(九) 异步队列模块 Deferred 详解

    deferred对象就是jQuery的回调函数解决方案,它解决了如何处理耗时操作的问题,比如一些Ajax操作,动画操作等.(P.s:紧跟上一节:https://www.cnblogs.com/grea ...

  6. Java并发系列[4]----AbstractQueuedSynchronizer源码分析之条件队列

    通过前面三篇的分析,我们深入了解了AbstractQueuedSynchronizer的内部结构和一些设计理念,知道了AbstractQueuedSynchronizer内部维护了一个同步状态和两个排 ...

  7. Python 源码分析:queue 队列模块

    起步 queue 模块提供适用于多线程编程的先进先出(FIFO)数据结构.因为它是线程安全的,所以多个线程很轻松地使用同一个实例. 源码分析 先从初始化的函数来看: 从这初始化函数能得到哪些信息呢?首 ...

  8. 【TencentOS tiny】深度源码分析(2)——调度器

    温馨提示:本文不描述与浮点相关的寄存器的内容,如需了解自行查阅(毕竟我自己也不懂) 调度器的基本概念 TencentOS tiny中提供的任务调度器是基于优先级的全抢占式调度,在系统运行过程中,当有比 ...

  9. 【TencentOS tiny】深度源码分析(1)——task

    任务的基本概念 从系统的角度看,任务是竞争系统资源的最小运行单元.TencentOS tiny是一个支持多任务的操作系统,任务可以使用或等待CPU.使用内存空间等系统资源,并独立于其它任务运行,理论上 ...

随机推荐

  1. 安卓手机360浏览器神奇bug,难以理解的

    今天渠道组,说广告在安卓手机360浏览器上显示不了,我就去排查这个问题,发现所有安卓浏览器还真看不到广告,本来以为是360浏览器屏蔽了,,但是另一个项目就没事,后来经过几个小时的不断alert断点调试 ...

  2. Postgresql-rman

    联机程序. 并且目标数据库必须处于归档模式. 支持在线全备, 增量备份, 归档备份 增量备份基于已经存在的一个全库备份 rman 本身使用pg_start_backup(), copy, pg_sto ...

  3. 【Redis】基础学习概览【汇总】

    一.概述 1.1 简介 1.2 Redis单线程好处 1.3 单线程弊端 1.4 Redis应用场景 二.安装.开启以及关闭 三.Redis基本数据类型 四.SpringBoot整合Redis 五.R ...

  4. Android-打包AAR步骤以及最为关键的注意事项!

    ### 简介 最近因为项目的要求,需要把开发的模块打包成aar,供其他项目调用,在搞了一段时间后,发现这里还是有很多需要注意的地方,所以记录一下,帮助大家不要走弯路. **首先何为aar包?** ![ ...

  5. Storm 系列(四)—— Storm 集群环境搭建

    一.集群规划 这里搭建一个 3 节点的 Storm 集群:三台主机上均部署 Supervisor 和 LogViewer 服务.同时为了保证高可用,除了在 hadoop001 上部署主 Nimbus ...

  6. 008 Python基本语法元素小结

    目录 一.概要 二.保留字 三.温度转换 一.概要 缩进.注释.命名.变量.保留字 数据类型.字符串. 整数.浮点数.列表 赋值语句.分支语句.函数 input().print().eval(). p ...

  7. java教程系列一:什么是Java语言?

    海上生明月,天涯共此时. Java是一种通用的计算机编程语言,它具有卓越的通用性.高效性.平台移植性和安全性.它旨在让应用程序开发人员"write once, run anywhere&qu ...

  8. 秒杀活动是否适合O2O生鲜行业的思考

    一.命题提出背景 公司是O2O生鲜行业,公司的业务部门提出要做秒杀活动.产品负责人听到后说没意义,秒杀不适合O2O生鲜.(产品负责人据说是阿里出来的P8,后来去微信,去永辉带运营.研发,做大佬,再后来 ...

  9. 加入百度地图遇到 framework not found BaiduMapAPI***

    新建一个文件夹,把需要的静态库和.h文件放到文件夹里面.就解决啦.虽然不知道为什么这样可以,但是可以使用啦~~~

  10. 从零开始入门 K8s| 阿里技术专家详解 K8s 核心概念

    作者| 阿里巴巴资深技术专家.CNCF 9个 TCO 之一 李响 一.什么是 Kubernetes Kubernetes,从官方网站上可以看到,它是一个工业级的容器编排平台.Kubernetes 这个 ...