一. ring blk 是什么

大家应该经常会听到 ringbuff , 那 ringblk(环形块缓冲区) 是什么呢?

ringblk 也是环形缓冲区,但是他的缓冲区的单位是一个 block , 每次对数据的操作都是以 block 为单位。

二. ringblk 怎么使用

ringblk 的使用先要创建环形块缓冲区,通过 alloc 申请到块之后,通过 put 写数据到缓冲区,通过 get 从缓冲区中获取数据。对于不需要在用的块,通过 free 释放。

连续的 block 可以组成 块队列

1. 创建块缓冲区

rt_rbb_t rt_rbb_create(rt_size_t buf_size, rt_size_t blk_max_num)

buf_size: 缓冲区大小

blk_max_num: 支持最大的 block 的胡亮

2. 销毁块缓冲区

void rt_rbb_destroy(rt_rbb_t rbb)

rbb :ringblk 的对象

3. 获取块缓冲区的大小

rt_size_t rt_rbb_get_buf_size(rt_rbb_t rbb)

rbb : 块缓冲区的对象

4. 从块缓冲区中分配一个块

rt_rbb_blk_t rt_rbb_blk_alloc(rt_rbb_t rbb, rt_size_t blk_size)

rbb : 块缓冲区的对象

blk_size : 块的大小

返回值:如果不为 NULL , 则表示返回分配的块对象

5. 将一个块放入到块缓冲区

void rt_rbb_blk_put(rt_rbb_blk_t block)

block :从块缓冲区申请出来的块对象

6. 从块缓冲区获取一个块

rt_rbb_blk_t rt_rbb_blk_get(rt_rbb_t rbb)

rbb : 块缓冲区的对象

返回值 :获取到块的对象

7. 释放一个块缓冲区上的块

void rt_rbb_blk_free(rt_rbb_t rbb, rt_rbb_blk_t block)

rbb : 块缓冲区的对象

block : 块对象

8 . 获取块队列

rt_size_t rt_rbb_blk_queue_get(rt_rbb_t rbb, rt_size_t queue_data_len, rt_rbb_blk_queue_t blk_queue)

rbb : 块缓冲区的对象

queue_data_len : 块队列中数据的最大长度,返回的长度必须小于它

blk_queue :块队列

块队列中的块缓冲区地址连续.

             tail                         head+------------------+---------------+--------+----------+--------+|      block3      |  empty1       | block1 |  block2  |fragment|+------------------+------------------------+----------+--------+|<-- return_size -->|    ||<--- queue_data_len --->|tail                          head+------------------+---------------+--------+----------+--------+|      block3      |  empty1       | block1 |  block2  |fragment|+------------------+------------------------+----------+--------+|<-- return_size -->|    out of len(b1+b2+b3)    ||<-------------- queue_data_len ---------------->|
9. 获取块队列的数据长度

rt_size_t rt_rbb_blk_queue_len(rt_rbb_blk_queue_t blk_queue)

blk_queue: 块队列对象

10. 块队列的缓冲区

rt_uint8_t *rt_rbb_blk_queue_buf(rt_rbb_blk_queue_t blk_queue)

blk_queue: 块队列对象

11. 释放块队列

void rt_rbb_blk_queue_free(rt_rbb_t rbb, rt_rbb_blk_queue_t blk_queue)

rbb : 块缓冲区

blk_queue :块队列对象

12. 下一个可以组成块队列的总长度

rt_size_t rt_rbb_next_blk_queue_len(rt_rbb_t rbb)

rbb : 块缓冲区的对象

三. 块缓冲区的实现

在分析原理之前先熟悉一下 ringblk 相关的结构体

struct rt_rbb_blk
{rt_rbb_status_t status :8; // 状态用 8 个bit 表示/* less then 2^24 */rt_size_t size :24; // 数据最长不能超过 2 的 24 次方rt_uint8_t *buf; // 块的缓冲区rt_slist_t list;// block 的链表
};
typedef struct rt_rbb_blk *rt_rbb_blk_t;
struct rt_rbb_blk_queue
{rt_rbb_blk_t blocks; // 连续的块rt_size_t blk_num;// block 的数量
};
typedef struct rt_rbb_blk_queue *rt_rbb_blk_queue_t;
struct rt_rbb
{rt_uint8_t *buf; // 块缓冲区的数据缓冲区rt_size_t buf_size; // buf 的大小/* all of blocks */rt_rbb_blk_t blk_set; // 块的集合rt_size_t blk_max_num; // 块的最大数量/* saved the initialized and put status blocks */rt_slist_t blk_list;// 链表
};
typedef struct rt_rbb *rt_rbb_t;

以下的源码都有删减:

1. 创建块缓冲区
rt_rbb_t rt_rbb_create(rt_size_t buf_size, rt_size_t blk_max_num)
{rbb = (rt_rbb_t)rt_malloc(sizeof(struct rt_rbb));// 申请块缓冲区的内存buf = (rt_uint8_t *)rt_malloc(buf_size);// 申请数据缓冲区的内存blk_set = (rt_rbb_blk_t)rt_malloc(sizeof(struct rt_rbb_blk) * blk_max_num);//申请最大 blk 数量的内存rt_rbb_init(rbb, buf, buf_size, blk_set, blk_max_num); // 初始化块缓冲区return rbb;
}
2. 销毁块缓冲区
void rt_rbb_destroy(rt_rbb_t rbb)
{// 释放申请的内存rt_free(rbb->buf);rt_free(rbb->blk_set);rt_free(rbb);}
3. 获取块缓冲区的大小
rt_size_t rt_rbb_get_buf_size(rt_rbb_t rbb)
{return rbb->buf_size; // 返回 块缓冲区的大小
}
4. 从块缓冲区分配一个块
rt_rbb_blk_t rt_rbb_blk_alloc(rt_rbb_t rbb, rt_size_t blk_size)
{rt_base_t level;rt_size_t empty1 = 0, empty2 = 0;rt_rbb_blk_t head, tail, new_rbb = NULL;level = rt_hw_interrupt_disable();// 关中断new_rbb = find_empty_blk_in_set(rbb); // 找到一个空闲块// 判断 申请出来的块是不是在 最大范围之内if (rt_slist_len(&rbb->blk_list) < rbb->blk_max_num && new_rbb){if (rt_slist_len(&rbb->blk_list) > 0) // 检查是不是第一次申请blk{   // 获取头节点的结构体起始地址head = rt_slist_first_entry(&rbb->blk_list, struct rt_rbb_blk, list);// 获取尾节点的结构体起始地址tail = rt_slist_tail_entry(&rbb->blk_list, struct rt_rbb_blk, list);if (head->buf <= tail->buf) // 头节点数据缓冲区的地址小于尾节点的数据缓存区的地址{/***                      head                     tail* +--------------------------------------+-----------------+------------------+* |      empty2     | block1 |   block2  |      block3     |       empty1     |* +--------------------------------------+-----------------+------------------+*                            rbb->buf*/// 求出空 block 的大小empty1 = (rbb->buf + rbb->buf_size) - (tail->buf + tail->size);empty2 = head->buf - rbb->buf;// 判断新的 block 可以存放的区域if (empty1 >= blk_size){ // 给 block 结构体赋值rt_slist_append(&rbb->blk_list, &new_rbb->list);new_rbb->status = RT_RBB_BLK_INITED;new_rbb->buf = tail->buf + tail->size;new_rbb->size = blk_size;}else if (empty2 >= blk_size){// 给 block 结构体赋值rt_slist_append(&rbb->blk_list, &new_rbb->list);new_rbb->status = RT_RBB_BLK_INITED;new_rbb->buf = rbb->buf;new_rbb->size = blk_size;}else{/* no space */new_rbb = NULL;}}else{/***        tail                                              head* +----------------+-------------------------------------+--------+-----------+* |     block3     |                empty1               | block1 |  block2   |* +----------------+-------------------------------------+--------+-----------+*                            rbb->buf*/// 获取空闲的空间empty1 = head->buf - (tail->buf + tail->size);// 判断剩余空间是否够本次的分配if (empty1 >= blk_size){// 给 block 结构体赋值rt_slist_append(&rbb->blk_list, &new_rbb->list);new_rbb->status = RT_RBB_BLK_INITED;new_rbb->buf = tail->buf + tail->size;new_rbb->size = blk_size;}else{   /* no space */new_rbb = NULL;}}}else{/* the list is empty */rt_slist_append(&rbb->blk_list, &new_rbb->list); // 把bew_rbb 链表插入到 rbbnew_rbb->status = RT_RBB_BLK_INITED; // 修改状态为 已经初始化new_rbb->buf = rbb->buf; // 设置缓冲区new_rbb->size = blk_size;// 设置块大小}}else{new_rbb = NULL;}rt_hw_interrupt_enable(level);return new_rbb;
}
5. 将一个块放入到块缓冲区
void rt_rbb_blk_put(rt_rbb_blk_t block)
{block->status = RT_RBB_BLK_PUT; // 设置状态为 PUT
}
6. 从块缓冲区获取一个块
rt_rbb_blk_t rt_rbb_blk_get(rt_rbb_t rbb)
{rt_base_t level;rt_rbb_blk_t block = NULL;rt_slist_t *node;if (rt_slist_isempty(&rbb->blk_list)) // 链表为空,就是没有数据,就无法获取到数据了return 0;level = rt_hw_interrupt_disable();// 关中断// 遍历链表for (node = rt_slist_first(&rbb->blk_list); node; node = rt_slist_next(node)){   // 获取当前 list 的结构体地址block = rt_slist_entry(node, struct rt_rbb_blk, list);if (block->status == RT_RBB_BLK_PUT) // 判断当前块的状态为 PUT{block->status = RT_RBB_BLK_GET; // 设置状态为 GETgoto __exit;}}/* not found */block = NULL;__exit:rt_hw_interrupt_enable(level); // 开中断return block; //返回 block
}
7. 释放块缓冲区上的块
void rt_rbb_blk_free(rt_rbb_t rbb, rt_rbb_blk_t block)
{/* remove it on rbb block list */rt_slist_remove(&rbb->blk_list, &block->list);// 从链表上脱离block->status = RT_RBB_BLK_UNUSED; // 设置状态为 未使用
}
8. 获取块队列
rt_size_t rt_rbb_blk_queue_get(rt_rbb_t rbb, rt_size_t queue_data_len, rt_rbb_blk_queue_t blk_queue)
{rt_base_t level;rt_size_t data_total_size = 0;rt_slist_t *node;rt_rbb_blk_t last_block = NULL, block;if (rt_slist_isempty(&rbb->blk_list))// 如果链表为空,就不会有块队列了return 0;level = rt_hw_interrupt_disable(); // 关中断// 遍历链表for (node = rt_slist_first(&rbb->blk_list); node; node = rt_slist_next(node)){if (!last_block) // 如果下一个 block 为空{   // 获取 list 节点上的结构体的地址last_block = rt_slist_entry(node, struct rt_rbb_blk, list);if (last_block->status == RT_RBB_BLK_PUT) // 如果状态是 PUT{/* save the first put status block to queue */blk_queue->blocks = last_block; // 保存第一个 blockblk_queue->blk_num = 0;}else{/* the first block must be put status */last_block = NULL; // 第一个 block 必须是 PUT 状态continue;}}else // 执行了 else 说明不是第一个 block 了{  //  获取的结构体的首地址block = rt_slist_entry(node, struct rt_rbb_blk, list);/** these following conditions will break the loop:* 1. the current block is not put status* 2. the last block and current block is not continuous* 3. the data_total_size will out of range*/if (block->status != RT_RBB_BLK_PUT ||last_block->buf > block->buf ||data_total_size + block->size > queue_data_len){break;}/* backup last block */last_block = block; // 下一个 block}/* remove current block */rt_slist_remove(&rbb->blk_list, &last_block->list); // 移除链表data_total_size += last_block->size; // 设置数据的总长度last_block->status = RT_RBB_BLK_GET; // 设置状态为 GETblk_queue->blk_num++;// 数量 +1}rt_hw_interrupt_enable(level); // 开中断return data_total_size; // 数据的总长度
}
9. 获取块队列的数据长度
rt_size_t rt_rbb_blk_queue_len(rt_rbb_blk_queue_t blk_queue)
{rt_size_t i, data_total_size = 0;for (i = 0; i < blk_queue->blk_num; i++){data_total_size += blk_queue->blocks[i].size; // 循环累加数据块的大小}return data_total_size;
}
10. 块队列的缓冲区
rt_uint8_t *rt_rbb_blk_queue_buf(rt_rbb_blk_queue_t blk_queue)
{return blk_queue->blocks[0].buf; // 返回块队列的数据的首地址
}
11. 释放块队列
void rt_rbb_blk_queue_free(rt_rbb_t rbb, rt_rbb_blk_queue_t blk_queue)
{rt_size_t i;for (i = 0; i < blk_queue->blk_num; i++){rt_rbb_blk_free(rbb, &blk_queue->blocks[i]); // 循环释放已申请的空间}
}
12. 下一个可以组成块队列的总长度
rt_size_t rt_rbb_next_blk_queue_len(rt_rbb_t rbb)
{rt_base_t level;rt_size_t data_len = 0;rt_slist_t *node;rt_rbb_blk_t last_block = NULL, block;if (rt_slist_isempty(&rbb->blk_list)) // 检查链表是否为空return 0;level = rt_hw_interrupt_disable(); // 关中断// 遍历链表for (node = rt_slist_first(&rbb->blk_list); node; node = rt_slist_next(node)){if (!last_block) // 第一次为 NULL 才可以进入这里的判断{   // 获取 last_lock 的结构体的首地址last_block = rt_slist_entry(node, struct rt_rbb_blk, list);if (last_block->status != RT_RBB_BLK_PUT){   // 检查块的状态/* the first block must be put status */last_block = NULL;continue;}}else{   // 获取 block 结构体块的首地址block = rt_slist_entry(node, struct rt_rbb_blk, list);/** these following conditions will break the loop:* 1. the current block is not put status* 2. the last block and current block is not continuous*/if (block->status != RT_RBB_BLK_PUT || last_block->buf > block->buf){break;}/* backup last block */last_block = block; // last_block 指向  block}data_len += last_block->size;// 累加 数据的长度}rt_hw_interrupt_enable(level); // 开中断return data_len; // 返回数据的长度
}

四. 总结

  1. ringblk: 是由 多个不同长度 的 block 组成的,ringbuff : 是由单字节的数据组成的。ringblk 每一个 block 有多少个字节可以由用户自己设定。
  2. ringblk 支持零字节拷贝(不需要额外的 memcpy 操作)。所以 rbb 非常适合用于生产者顺序 put 数据块,消费者顺序 get 数据块的场景,例如:DMA 传输,通信帧的接收与发送等等。

RT-Thread 隐藏的宝藏之ringblk相关推荐

  1. 关于RT thread系统节拍时钟的配置

    关于RT thread系统节拍时钟的配置                  -----本文基于rt-thread-3.1.3版本编写 首先,使用RTthread OS时,要配置(或者明白)它的系统节拍 ...

  2. rt thread studio使用QBOOT和片外flash实现OTA升级

    我们这里要使用单片机外部flash作为OTA的下载分区,外部flash硬件连接关系 PB3-->SPI3_CLK PB4-->SPI3_MISO PB5-->SPI3_MOSI PE ...

  3. rt thread 使用FAL遇到fal_init() undefined reference

    rt thread FAL 0.5版,之前有没有不知道,遇到一个坑. 在main.cpp里面已经 #include <fal.h> fal_init() 编译报错,说 fal_init() ...

  4. RT Thread Free Modbus移植问题整理

    RT Thread Free Modbus移植问题整理 问题描述: 在读写寄存器中,写数据正常,只能读1个寄存器的值,多个值会异常. 在移植过程中发现串口(或RS485)数据接收长度异常. 一.环境描 ...

  5. Yeelink平台使用——远程控制 RT Thread + LwIP+ STM32

    1.前言     [2014年4月重写该博文]     经过若干时间的努力终于搞定了STM32+LwIP和yeelink平台的数据互通,在学习的过程中大部分时间花在以太网协议栈学习上,但是在RT Th ...

  6. RT Thread根据开发板制作BSP方法

    之前一直不懂怎么使用RT Thread的软件包,感谢网上的大神,看了你们的博客后大概了解一些,在此做下记录.用RT Thread软件包需要RT Thread的系统,但是RT Thread和RT Thr ...

  7. RT Thread之 Uart2 操作

    官网连接:https://docs.rt-thread.org/#/rt-thread-version/rt-thread-standard/programming-manual/device/uar ...

  8. 基于rt thread smart构建EtherCAT主站

    我把源码开源到到了gitee,https://gitee.com/rathon/rt-thread-smart-soem 有兴趣的去可以下载下来跑一下 软件工程推荐用vscode 打开.rt thre ...

  9. RT Thread利用STM32CUBEMX和RT Thread studio来创建模板工程

    (1)RT Thread利用STM32CUBEMX来创建模板工程 1.参考文档: 基于 CubeMX 移植 RT-Thread Nano:RT-Thread 文档中心 注意:串口2必须使能异步模式(启 ...

  10. rt thread系统下添加wiznet软件包后,不插网线CPU利用率100%问题

    rt thread系统下添加wiznet软件包后如果不插网线的话其他任务运行很卡,使用ps命令发现优先级低的任务很多都超时了 rt thread线程错误码 添加了一个可以查看CPU利用率的软件包CPU ...

最新文章

  1. JAVA各种并发锁从synchronized 到CAS 到 AQS
  2. 24 GISer必备知识(一) 坐标系
  3. 曾经的独角兽 Docker,如今资金紧张
  4. 基于JAVA+SpringMVC+Mybatis+MYSQL的在线论坛管理系统
  5. 淘宝质量属性场景分析
  6. CentOS 下无线网卡的安装和使用
  7. AI佳作解读系列(二)——目标检测AI算法集杂谈:R-CNN,faster R-CNN,yolo,SSD,yoloV2,yoloV3...
  8. python输出列表元素_怎样用一行python打印列表所有元素
  9. the CBD process terminated
  10. Linux固态硬盘 设置写入缓存,写入缓存策略怎么打开让SSD提高速度
  11. Android studio断点调试源码
  12. 关于Android 12 适配,看这篇就够了
  13. 读刘文鹏之《古代埃及史》
  14. 7-301 sdut- C语言实验-数组逆序(数组移位)
  15. 利用Excel计算DAU、商品转化率和ARPU值等
  16. 搭建CocoaPods私有库
  17. [转]局域网共享一键修复 18.5.8 https://zhuanlan.zhihu.com/p/24178142
  18. tems测试软件不显示小区标,TEMS回放LOG文件GSM Serving+Neighbors[MS1]窗口无法显示小区号...
  19. 大学生可以享受到的相关学生优惠权益收集整理
  20. openssl SM2 加解密的坑

热门文章

  1. 【Word】word中文本输在公式外边,输入数学公式后如何在后面正常输入文字
  2. 某程序员吐槽:买房自己家出400万首付,女朋友家里一分不出还要求加名字,怎么说服女朋友放弃写名?...
  3. java 百度云对象存储STS鉴权
  4. Photoshop制作的海报修改~
  5. 电商之库存超卖或者秒杀超卖问题
  6. php开发支付宝支付密码忘记了怎么办_PHP实现个人支付宝支付开发(一)
  7. ILI9341的使用之【二】ILI9341介绍
  8. linux usleep 函数,Linux 高精確的時序(sleep, usleep,nanosleep)
  9. Shell中单引号和双引号的使用
  10. 生猛!这篇万字长文,一下子把计算机底层知识说明白了!