0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

TencentOS-tiny中环形队列的实现

strongerHuang 来源:Mculover666 作者:Mculover666 2021-10-08 16:30 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

1. 什么是队列队列(queue)是一种只能在一端插入元素、在另一端删除元素的数据结构,遵循「先入先出」(FIFO)的规则。

队列中有两个基本概念:

队头指针(可变):永远指向此队列的第一个数据元素;

队尾指针(可变):永远指向此队列的最后一个数据元素;

队列中的数据存储方式有两种:

① 基于静态连续内存(数组)存储,如图:② 基于动态内存(链表节点)存储,如图:

后续都使用基于静态内存存储的队列讲解。

❞队列提供两个统一的操作:

「入队(enqueue)」

入队将一个元素添加到队尾,并将队尾指针+1后移,如图:

「出队(dequeue)」

出队将从队头中取出一个元素,并将队头指针+1后移,如图:

2. 环形队列2.1. 环形队列的特点

普通队列的入队操作将队尾指针后移+1,出队操作将队头指针后移+1,操作几次之后会发现队头指针和队尾指针都跑到缓冲区的尾部去了:这就导致了前面的内存空间全被浪费,如果要重新恢复使用,则需要进行元素和指针的移动:显然这种队列使用方式太不方便了,所以就诞生了环形队列:「不用搬移元素和指针,一直可以重复利用这段内存空间」。

2.2. 环形队列的实现

TencentOS-tiny中环形队列的实现在tos_ring_queue.h和tos_ring_queue.c中。

typedef struct k_ring_queue_st {

knl_obj_t knl_obj;

uint16_t head; //队头指针

uint16_t tail; //队尾指针

size_t total; //记录队列中元素的个数

uint8_t *pool; //队列底层的存储结构(一个数组)

size_t item_size; //队列中每个元素的大小,单位:字节

size_t item_cnt; //队列中可以容纳的元素数量

} k_ring_q_t;

环形队列初始化,将队头指针和队尾置0:

__API__ k_err_t tos_ring_q_create(k_ring_q_t *ring_q, void *pool, size_t item_cnt, size_t item_size)

{

//省略了参数合法性检查代码

ring_q-》head = 0u;

ring_q-》tail = 0u;

ring_q-》total = 0;

ring_q-》pool = (uint8_t *)pool;

ring_q-》item_size = item_size;

ring_q-》item_cnt = item_cnt;

return K_ERR_NONE;

}

判断环形队列是否为满或者为空:

__API__ int tos_ring_q_is_empty(k_ring_q_t *ring_q)

{

TOS_CPU_CPSR_ALLOC();

int is_empty = K_FALSE;

//省略了参数合法性检查代码

TOS_CPU_INT_DISABLE();

is_empty = (ring_q-》total == 0 ? K_TRUE : K_FALSE);

TOS_CPU_INT_ENABLE();

return is_empty;

}

__API__ int tos_ring_q_is_full(k_ring_q_t *ring_q)

{

TOS_CPU_CPSR_ALLOC();

int is_full = K_FALSE;

//省略了参数合法性检查代码

TOS_CPU_INT_DISABLE();

is_full = (ring_q-》total == ring_q-》item_cnt ? K_TRUE : K_FALSE);

TOS_CPU_INT_ENABLE();

return is_full;

}

环形队列入队操作的API如下:

__API__ k_err_t tos_ring_q_enqueue(k_ring_q_t *ring_q, void *item, size_t item_size);

在此API中,入队操作的实现如下:

__STATIC_INLINE__ void ring_q_item_increase(k_ring_q_t *ring_q)

{

ring_q-》tail = RING_NEXT(ring_q, ring_q-》tail);

++ring_q-》total;

}

环形队列出队操作的API如下:

__API__ k_err_t tos_ring_q_dequeue(k_ring_q_t *ring_q, void *item, size_t *item_size);

在此API中,出队操作的实现如下:

__STATIC_INLINE__ void ring_q_item_decrease(k_ring_q_t *ring_q)

{

ring_q-》head = RING_NEXT(ring_q, ring_q-》head);

--ring_q-》total;

}

在入队和出队操作的时候都使用了 RING_NEXT 宏,用来获取在环形队列中的下一个位置:

#define RING_NEXT(ring_q, index) ((index + 1) % ring_q-》item_cnt)

2.3. 环形队列使用Demo

编写如下的测试代码:

#include 《tos_k.h》typedef struct item_st {

int a;

int b;

int c;

} item_t;

#define RING_QUEUE_ITEM_MAX 5uint8_t ring_q_buffer[RING_QUEUE_ITEM_MAX * sizeof(item_t)];

k_ring_q_t ring_q;

void entry_task_demo(void *arg)

{

k_err_t err;

int i;

item_t item;

size_t item_size;

//创建环形队列

tos_ring_q_create(&ring_q, ring_q_buffer, RING_QUEUE_ITEM_MAX, sizeof(item_t));

//数据入队

for(i = 0;i 《 RING_QUEUE_ITEM_MAX; i++)

{

item.a = i;

item.b = i;

item.c = i;

err = tos_ring_q_enqueue(&ring_q, &item, sizeof(item_t));

if(err == K_ERR_NONE)

{

printf(“enqueue a item: %d %d %d

”, item.a, item.b, item.c);

}

else

{

printf(“ring queue enqueue fail,err = %d

”, err);

}

}

//队列满之后,继续入队

err = tos_ring_q_enqueue(&ring_q, &item, sizeof(item_t));

if(err == K_ERR_RING_Q_FULL)

{

printf(“ring queue is full: %s

”, tos_ring_q_is_full(&ring_q) ? “TRUE” : “FALSE”);

}

else

{

printf(“ring queue enqueue fail,err = %d

”, err);

}

//数据出队

for(i = 0; i 《 RING_QUEUE_ITEM_MAX; ++i)

{

err = tos_ring_q_dequeue(&ring_q, &item, &item_size);

if(err == K_ERR_NONE)

{

printf(“dequeue a item(%d bytes): %d %d %d

”, item_size, item.a, item.b, item.c);

}

else

{

printf(“ring queue dequeue fail,err = %d

”, err);

}

}

//没有数据后继续出队

err = tos_ring_q_dequeue(&ring_q, &item, &item_size);

if(err == K_ERR_RING_Q_EMPTY)

{

printf(“ring queue is empty: %s

”, tos_ring_q_is_empty(&ring_q) ? “TRUE” : “FALSE”);

}

else

{

printf(“ring queue dequeue fail,err = %d

”, err);

}

}

运行结果如下:

3. 优先级队列3.1. 优先级队列的特点

优先级队列也是一种基于队列的数据结构,但是它「不遵循FIFO」,而是按照每个元素的优先级进行出队:「最高优先级的先出队」。

3.2. 优先级队列的实现

TencentOS-tiny中环形队列的实现在tos_prio_queue.h和tos_prio_queue.c中。

优先级队列在数据入队的时候,会按照入队元素的优先级进行一次排序,「将优先级值最小(优先级最高的元素)放在队头」,出队的时候只需要取第一个元素即可。

正是因为这种特性,优先级队列的底层存储结构不能使用数组(排序太麻烦),而是使用了二项堆的数据结构。

二项堆是一种二叉树集合的数据结构,在本文中不再深入讲解,有兴趣的读者可以自己搜索阅读。

❞下面只给出优先级队列的API,「理解其规则,会用即可」。

创建优先级队列

__API__ k_err_t tos_prio_q_create(k_prio_q_t *prio_q, void *mgr_array, void *pool, size_t item_cnt, size_t item_size);

参数描述

prio_q优先级队列控制块指针

mgr_array提供一块缓冲区用于内部管理

pool队列的缓冲区

item_cnt队列可容纳的元素数量

item_size每个元素的大小,单位字节

其中用于内部管理的缓存区大小可以使用宏定义来计算,比如有5个元素的管理缓冲区大小:

uint8_t mgr_pool[TOS_PRIO_Q_MGR_ARRAY_SIZE(5)];

元素入队

__API__ k_err_t tos_prio_q_enqueue(k_prio_q_t *prio_q, void *item, size_t item_size, k_prio_t prio);

其中优先级的值遵循:数值越小,优先级越高。

元素出队

__API__ k_err_t tos_prio_q_dequeue(k_prio_q_t *prio_q, void *item, size_t *item_size, k_prio_t *prio);

其中prio需要传入一个地址,用于记录出队元素的优先级。

3.3. 优先级队列使用Demo

#include 《tos_k.h》typedef struct item_st {

int a;

int b;

int c;

} item_t;

#define PRIO_QUEUE_ITEM_MAX 5uint8_t prio_q_buffer[PRIO_QUEUE_ITEM_MAX * sizeof(item_t)];

uint8_t mgr_pool[TOS_PRIO_Q_MGR_ARRAY_SIZE(PRIO_QUEUE_ITEM_MAX)];

k_prio_q_t prio_q;

void entry_task_demo(void *arg)

{

k_err_t err;

int i;

item_t item;

size_t item_size;

k_prio_t item_prio;

//创建优先级队列

tos_prio_q_create(&prio_q, mgr_pool, prio_q_buffer, PRIO_QUEUE_ITEM_MAX, sizeof(item_t));

//数据入队

for(i = PRIO_QUEUE_ITEM_MAX;i 》 0; i--)

{

item.a = i;

item.b = i;

item.c = i;

err = tos_prio_q_enqueue(&prio_q, &item, sizeof(item), i);

if(err == K_ERR_NONE)

{

printf(“enqueue a item: %d %d %d

”, item.a, item.b, item.c);

}

else

{

printf(“prio queue enqueue fail,err = %d

”, err);

}

}

//队列满之后,继续入队

err = tos_prio_q_enqueue(&prio_q, &item, sizeof(item_t), i);

if(err == K_ERR_PRIO_Q_FULL)

{

printf(“prio queue is full: %s

”, tos_prio_q_is_full(&prio_q) ? “TRUE” : “FALSE”);

}

else

{

printf(“prio queue enqueue fail,err = %d

”, err);

}

//数据出队

for(i = 0; i 《 PRIO_QUEUE_ITEM_MAX; ++i)

{

err = tos_prio_q_dequeue(&prio_q, &item, &item_size, &item_prio);

if(err == K_ERR_NONE)

{

printf(“dequeue a item[piro %d]: %d %d %d

”, item_prio, item.a, item.b, item.c);

}

else

{

printf(“prio queue dequeue fail,err = %d

”, err);

}

}

//没有数据后继续出队

err = tos_prio_q_dequeue(&prio_q, &item, &item_size, &item_prio);

if(err == K_ERR_PRIO_Q_EMPTY)

{

printf(“prio queue is empty: %s

”, tos_prio_q_is_empty(&prio_q) ? “TRUE” : “FALSE”);

}

else

{

printf(“prio queue dequeue fail,err = %d

”, err);

}

}

4. 总结① 普通队列是一种只能在一端入队,在一端出队的数据结构,规则:FIFO。

② 环形队列对内存空间的利用率最高,使用最多,规则:FIFO。

③ 优先级队列不遵循FIFO,每个元素都有自己的优先级,规则:优先级最高的元素先出队。

责任编辑:haq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    7357

    浏览量

    95108
  • 存储
    +关注

    关注

    13

    文章

    4917

    浏览量

    90358
  • TencentOS
    +关注

    关注

    0

    文章

    8

    浏览量

    7565

原文标题:TencentOS-tiny中队列、环形队列、优先级队列的实现及使用

文章出处:【微信号:strongerHuang,微信公众号:strongerHuang】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    ESP32Cx-TINY系列立创开源啦!

    28×69mm,一个U盘的面积,覆盖ESP32-C2/C3/C5/C61四款芯片,全系列MIT协议开源。启明TINY工作室带来了C系列乐鑫口袋开发板家族。我们为什么做TINY系列我们的初衷其实很简单
    的头像 发表于 03-30 18:15 574次阅读
    ESP32Cx-<b class='flag-5'>TINY</b>系列立创开源啦!

    RDMA设计43:队列删除及连接断开功能测试

    在接收到正确的删除队列请求后,首先进入连接断开流程,即四次挥手断链,如图中红框部分。随后进行队列删除操作。返回队列操作状态 0x2b,符合设计预期,验证通过。
    的头像 发表于 02-24 07:50 705次阅读
    RDMA设计43:<b class='flag-5'>队列</b>删除及连接断开功能测试

    RDMA设计25:队列管理模块之发送模块详细设计分析

    发送队列存储为所有发送队列共用的存储空间,根据用户环境和开发板环境不同可由 BRAM、URAM 或 LUTRAM 实现。发送队列管理单元则负责管理这个存储空间,并处理用户指令和发送
    的头像 发表于 01-25 16:27 5892次阅读
    RDMA设计25:<b class='flag-5'>队列</b>管理模块之发送模块详细设计分析

    RDMA设计26:队列管理模块设计之接收队列模块详细分析

    本文主要交流设计思路,在本博客已给出相关博文100多篇,希望对初学者有用。注意这里只是抛砖引玉,切莫认为参考这就可以完成商用IP设计。 (2)接收队列 接收队列由一个接收队列管理单元组成。与发送
    发表于 01-22 09:03

    RDMA设计24:队列管理模块设计

    队列管理模块采用管理与存储分离的结构进行设计,由发送队列存储、发送队列管理、接收队列管理、完成条目解析、异常完成条目处理和 Round-Robin 仲裁组成。
    的头像 发表于 01-20 11:45 1612次阅读
    RDMA设计24:<b class='flag-5'>队列</b>管理模块设计

    RDMA设计17:队列管理模块设计2

    。 (2)接收队列 接收队列由一个接收队列管理单元组成。与发送队列类似的是,接收队列管理单元也由若干表单构成,其中包括 RQ1 表单和用户
    发表于 01-04 14:54

    NVMe高速传输之摆脱XDMA设计54:如何测试队列管理功能2

    , 表示操作顺序错误; 创建 I/O 完成队列和提交队列后, 先删除完成队列, 返回错误值为 8, 表示操作顺序错误。 打印信息显示测试结果与预期设计功能一致, 成功执行了队列的创建和
    发表于 12-10 08:33

    优先级队列介绍

    队列(Queue)的知识点:「概念」:队列是一种先进先出(FIFO)的数据结构,类似于排队的概念。「基本操作」:enqueue(item): 将元素添加到队列的末尾。dequeue(): 从
    发表于 11-26 07:56

    如何不用olimex ARM-USB-TINY-H debugger实现调试?

    DDR的bsp文件,请问github里的bsp文件是否兼容所有板子,而且我只有一根usb线可以接JTAG口,如何不用olimex ARM-USB-TINY-H debugger实现调试? fpga小白谢谢大家啦。
    发表于 11-10 08:15

    基于环形队列的UART收发回显实验

    问题。在本实验中,我们使用环形队列实现实验1的串口收发回显,将串口接收到的数据暂存在队列中,待完成一次接收后再将队列中的数据全部发出去。
    的头像 发表于 10-27 13:51 2195次阅读
    基于<b class='flag-5'>环形</b><b class='flag-5'>队列</b>的UART收发回显实验

    【RA4M2-SENSOR】—— 13.串口实现循环队列

    上节我已经讲解并实现了使用串口实现不定长度数据的接收,本篇帖子我来采用环形队列实现不定长度数据的接收和发送。 循环
    发表于 09-01 11:46

    NVMe IP高速传输却不依赖XDMA设计之九:队列管理模块(上)

    这是采用PCIe设计NVMe,并非调用XDMA方式,后者在PCIe4.0时不大方便,故团队直接采用PCIe设计,结合UVM验证加快设计速度。 队列管理模块采用队列的存储与控制分离的设计结构。
    的头像 发表于 08-04 09:53 884次阅读
    NVMe IP高速传输却不依赖XDMA设计之九:<b class='flag-5'>队列</b>管理模块(上)

    NVMe高速传输之摆脱XDMA设计九:队列管理模块设计(上)

    条目,一个提交队列管理单元用于实现提交队列存储地址空间的管理和门铃控制。在提交队列管理单元中,构建一个admin提交队列表单和N个I/O提交
    发表于 07-27 17:41

    RabbitMQ消息队列解决方案

    在现代分布式系统架构中,消息队列作为核心组件,承担着系统解耦、异步处理、流量削峰等重要职责。RabbitMQ作为一款成熟的消息队列中间件,以其高可用性、高可靠性和丰富的特性,成为众多企业的首选方案。本文将从运维工程师的角度,详细阐述RabbitMQ从单机部署到集群搭建的完
    的头像 发表于 07-08 15:55 796次阅读

    JCMsuite应用:光学环形谐振腔模拟

    本案程演示了环形谐振腔的模拟。这种类型的集成光子器件,例如用作升/降滤波器或在传感应用中,当物质或粒子附着在环上时,通过测量其共振频率的位移来检测: 对于集成光子电路中的无源光器件,s矩阵通常是
    发表于 06-11 08:46