一种基于镜像指示位办法的RingBuffer实现,解决Mirror和2的幂个数限制

简介

在嵌入式开发中,经常有需要用到RingBuffer的概念,在RingBuffer中经常遇到一个Buffer满和Buffer空的判断的问题,一般的做法是留一个单位的buffer不用,这样做最省事,但是当RingBuffer单位是一个结构体时,这个浪费就不能接受了,市面上大多数解决办法是镜像指示位办法,但是具体实现上又有各种设计,但是并不是满足的开发需要,所以有本项目。

本项目地址:bobwenstudy/simple_ringbuffer: 一种基于镜像指示位办法的RingBuffer实现,解决Mirror和2的幂个数限制 (github.com),参考资料有:环形缓冲器 - 维基百科,自由的百科全书 (wikipedia.org),ring buffer,一篇文章讲透它? - 知乎 (zhihu.com)。

对比现有的实现,本项目的特点是。

类别simple_ringbufferkfifo(linux)ringbuffer (rt-thread)
需要mirror位(多进程风险)
需要个数为2的幂
支持结构体成员是(linux5.7)

mirror位

多了Mirror位就会有一个多进程操作的风险,除非Mirror位和数值同时写入。所以最好不要有Mirror位。

个数为2的幂

Linux的kfifo解决了Mirror位的问题,因为其用到了uint32_t回环的特性,需要个数为2个幂。虽然这样大大减少了算法工作量,也可以用位运算来优化取余预算的计算效率。但是使用起来多少不是很舒适,尤其设计到对结构体成员操作时,一不注意又要浪费Buffer。

支持结构体成员

其实如果RingBuffer成员的单位为1个字节的话,其实没必要在乎1个字节的损失,只是使用时需要多申请1个字节,多少看起来不是很清爽。

但是当RingBuffer的成员的单位为很大的值时,1个成员的损失才至关重要。

现有的项目考虑灵活性,RingBuffer需要支持各种字节操作,效率并不高,本项目针对结构体操作需要,专门设计了Data_RingBuffer工具来对多字节场景进行处理,操作效率更高,并提供了2种操作接口,以满足不同业务操作的需要。

镜像指示位-本项目实现

本项目不想有Mirror的操作问题,也不想有个数2的幂的限制。处理上做了一些特殊的处理,严格区分index和ptr的概念。

index的取值范围为[0~2n-1],并不像Linux取到最大值,解决了个数2的幂的限制。

write_index = ringbuf->write_index + len;
if (write_index >= (ringbuf->total_size << 1))
{write_index -= (ringbuf->total_size << 1);
}
ringbuf->write_index = write_index;

其中ptr的获取,考虑效率使用减法,不使用取余运算。

#define RINGBUFFER_INDEX_TO_PTR(_index, _total_size)                                               \((_index >= _total_size) ? (_index - _total_size) : (_index))
uint32_t wptr = RINGBUFFER_INDEX_TO_PTR(ringbuf->write_index, ringbuf->total_size);

代码结构

代码结构如下所示:

  • simple_ringbuffer:Ringbuffer实现,包含结构体操作实现simple_data_ringbuffer和缓冲池操作实现simple_data_ringbuffer
  • test_0.ctest_1.ctest_2.c:测试例程。
  • main.c:测试例程。
  • build.mkMakefile:Makefile编译环境。
  • README.md:说明文档
simple_ringbuffer├── simple_ringbuffer│   ├── simple_data_ringbuffer.c│   ├── simple_data_ringbuffer.h│   ├── simple_ringbuffer.c│   └── simple_ringbuffer.h├── build.mk├── code_format.py├── LICENSE├── main.c├── Makefile├── README.md├── test_0.c└── test_1.c

使用说明

具体如何使用直接看例程就行,非常简单,看函数名和变量名即可。

单字节操作

使用提供simple_ringbuffer.h接口操作即可。

// Define ringbuf.
SIMPLE_RINGBUFFER_DEFINE(test_ringbuf, 0x100);// Put data to ringbuf.
uint8_t data[0x10];
simple_ringbuffer_put(&test_ringbuf, data, sizeof(data));// Get data from ringbuf.
uint8_t rdata[0x10];
simple_ringbuffer_get(&test_ringbuf, rdata, sizeof(rdata));

结构体操作

使用提供simple_data_ringbuffer.h接口操作即可。提供了两种接口,按需使用。

struct test_user_data
{uint8_t data[0x10];
};// Define ringbuf.
SIMPLE_DATA_RINGBUFFER_DEFINE(test_ringbuf, 0x100, sizeof(struct test_user_data));// API1
// Put data to ringbuf.
struct test_user_data data;
simple_ringbuffer_put(&test_ringbuf, &data);// Get data to ringbuf.
struct test_user_data rdata;
simple_ringbuffer_get(&test_ringbuf, &rdata);// API2
// Enqueue data to ringbuf.
struct test_user_data *data = NULL;
uint16_t index = simple_data_ringbuffer_enqueue_get(&test_ringbuf, (void **)&data); // enqueue getsimple_data_ringbuffer_enqueue(&test_ringbuf, index); // real enqueue// Dequeue data from ringbuf.
struct test_user_data *data;
data = simple_data_ringbuffer_dequeue_peek(&test_ringbuf); // dequeue peeksimple_data_ringbuffer_dequeue(&test_ringbuf); // real dequeue

缓存池操作

ringbuffer必须先入先出,在部分不是先入先出场景下,又想用RingBuffer读写线程独立的特性,本项目提供了一个简易数据缓存池实现方案,通过只保存数据指针的方式,来实现非先入先出的数据缓冲池。

其结构体如下。simple_pool_t用于缓冲池管理,由于RingBuffer存储的是指针,所以需要通过item_size记录每个成员的实际大小。定义一个Pool时,需要指针数组_name##_fifo_storage[_num],其用于存储实际存放数据的指针,用RingBuffer管理。真实存数据的区域为_name##_data_storage[_num][MROUND(_data_size)]

typedef struct simple_pool
{simple_data_ringbuffer_t ringbuf;uint16_t item_size;
} simple_pool_t;#define SIMPLE_POOL_DEFINE(_name, _num, _data_size)                                                \static simple_pool_t _name;                                                                    \static void *_name##_fifo_storage[_num];                                                       \static uint8_t _name##_data_storage[_num][MROUND(_data_size)];

使用操作如下:

struct test_user_data
{uint8_t data[0x100];
};// Define pool.
SIMPLE_POOL_DEFINE(test_pool, 0x10, sizeof(struct test_user_data));// Init pool.
SIMPLE_POOL_INIT(test_pool, 0x10, sizeof(struct test_user_data));// Get data from pool.
struct test_user_data *data;
SIMPLE_POOL_DEQUEUE(&test_pool, data);// Put data to pool.
SIMPLE_POOL_ENQUEUE(&test_pool, data);

测试说明

环境搭建

本项目支持Windows和Linux编译,同时支持Code Space在线编译,如果不想搭建环境可以直接CodeSpace编译。

Windows编译

目前需要安装如下环境:

  • GCC环境,笔者用的msys64+mingw,用于编译生成exe,参考这个文章安装即可。Win7下msys64安装mingw工具链 - Milton - 博客园 (cnblogs.com)。

GitHub-CodeSpace编译

直接在线编译即可。

编译说明

本项目都是由makefile组织编译的,编译整个项目只需要执行make all即可。

也就是可以通过如下指令来编译工程:

make all

而后运行执行make run即可运行例程,例程中实现了上述文档说明的问题和API的基本测试。

PS D:\workspace\github\simple_ringbuffer> make run
Compiling  : "test_0.c"
Compiling  : "test_1.c"
Linking    : "output/main.exe"
Building   : "output/main.exe"
Start Build Image.
objcopy -v -O binary output/main.exe output/main.bin
copy from `output/main.exe' [pei-i386] to `output/main.bin' [binary]
objdump --source --all-headers --demangle --line-numbers --wide output/main.exe > output/main.lst
Print Sizetext    data     bss     dec     hex filename118200  265384    2644  386228   5e4b4 output/main.exe
./output/main.exe
Testing test_work .......................................................... pass
Testing test_work_insuff ................................................... pass
Testing test_work_invalid .................................................. pass
Testing test_work_full ..................................................... pass
Testing test_work_full_define .............................................. pass
Testing test_work_read_index_big_to_write_index ............................ pass
Testing test_work_read_index_big_to_write_index ............................ pass
Testing test_work_odd ...................................................... pass
Testing test_work_insuff_odd ............................................... pass
Testing test_work_invalid_odd .............................................. pass
Testing test_work_full_odd ................................................. pass
Testing test_work_read_index_big_to_write_index_odd ........................ pass
Testing test_data_work ..................................................... pass
Testing test_data_work_full ................................................ pass
Testing test_data_work_full_define ......................................... pass
Testing test_data_work_full_define_enqueue ................................. pass
Testing test_data_work_odd ................................................. pass
Testing test_data_work_full_odd ............................................ pass
Executing 'run: all' complete!

可以看到,所有涉及到测试都通过。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/811728.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式-外观模式(Facade)

1. 概念 外观模式&#xff08;Facade Pattern&#xff09;是一种结构型设计模式&#xff0c;它提供了一个统一的接口&#xff0c;用于访问子系统中的一群接口。外观模式的主要目的是隐藏系统的复杂性&#xff0c;通过定义一个高层级的接口&#xff0c;使得子系统更容易被使用。…

房屋鉴定研究院报告系统

一、项目背景与意义 随着城市化进程的加速和房地产市场的蓬勃发展&#xff0c;房屋安全问题日益受到社会各界的广泛关注。房屋鉴定作为确保房屋安全的重要手段&#xff0c;对于保障人民群众生命财产安全、维护社会稳定具有重要意义。然而&#xff0c;传统的房屋鉴定方式存在诸…

webpack-loader的使用

引入css后执行打包命令 "build": "npx webpack --config wk.config.js"发现报错&#xff1a; webpack默认只能处理js其他的像css,图片都需要借助loader来处理 css-loader loader可以用于对模块的源代码进行转换&#xff0c;可以把css看成一个模块&…

AWS被误扣费了,怎么解决?

有时在使用aws时&#xff0c;可能会无意中被AWS扣费&#xff0c;对于如何处理这个问题&#xff0c;作为aws的合作伙伴&#xff0c;接下来由九河云进行讲解&#xff1a; &#xff08;1&#xff09;审查账单&#xff1a;首先&#xff0c;您需要仔细审查AWS账单&#xff0c;了解具…

并发学习27--多线程 Tomcat 线程池

Tomcat连接器的线程池 socketProcessor也是个线程 Executor处理线程是按照JDK线程池方法处理&#xff0c;优先选用核心线程&#xff0c;再用救急线程&#xff0c;再去阻塞队列&#xff0c;最后采用拒绝策略。 Tomcat线程池与ThreadExecutorPool的区别 Tomcat中的配置 Tomcat …

kafka快速入门+应用

Kafka, 构建TB级异步消息系统 1.快速入门 1.1 阻塞队列 在生产线程 和 消费线程 之间起到了 &#xff0c; 缓冲作用&#xff0c;即避免CPU 资源被浪费掉 BlockingQueue 解决 线程通信 的问题阻塞方法 put 、 take生产者、消费者 模式 生产者&#xff1a;产生数据的线程…

登录加载动画

实现登录中 … 三个点的loading动画 <template><div><el-input type"password" placeholder"请填写密码" autocomplete"new-password"v-model"password" keyup.enter.native"login" show-password clearable…

Word中图表题注样式自动编号

需求 在写论文的时候&#xff0c;希望图表题注是下面的样子&#xff0c;其中图号表示为&#xff1a;章的编号-本章中图的序号&#xff0c;而且都是小写数字。 网上找的方法大多是使用 “插入题注” 来插入&#xff0c;此时章的编号是大写的&#xff0c;如“图一-1”。然后再通…

后台权限控制及动态路由

需求 后台系统需要能实现不同的用户权限可以看到不同的功能。 用户只能使用他的权限所允许使用的功能。 功能设计 之前在我的SpringSecurity的课程中就介绍过RBAC权限模型。没有学习过的可以去看下 RBAC权限模型 。这里我们就是在RBAC权限模型的基础上去实现这个功能。 表分…

Web前端-HTML

黑马程序员JavaWeb开发教程 一、初识web前端 1、 标准也称为网页标准&#xff0c;由一系列的标准组成&#xff0c;大部分由W3C负责指定 2、 三个部分组成 HTML&#xff1a;负责网页的结构&#xff08;页面元素和内容&#xff09;CSS&#xff1a;负责网页的表现&#xff08;页…

STL--pair 数对

pair 数对&#x1f357; pair是一个模板类,使用时需要引用文件 #include <utility>//通用工具pair可将两个value处理为一个元素。C标准库内多处用到了这个结构。尤其容器 map、unordered_map和unordered_multimap就是使用pair来管理其内部元素(key_value),任何函数如果…

ppt技巧:如何将Word文档大纲中导入到幻灯片中?

在PowerPoint中&#xff0c;将Word文档的大纲导入到新的幻灯片是一种非常实用的技巧。以下是详细的步骤&#xff1a; 首先&#xff0c;需要打开PowerPoint软件并打开原始的幻灯片文件。 在PowerPoint的顶部【开始】菜单栏中&#xff0c;找到并点击“新建幻灯片”按钮&#xff0…

【力扣】142. 环形链表 II

142. 环形链表 II 题目描述 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&am…

微信小程序全屏开屏广告

效果图 代码 <template><view><!-- 自定义头部 --><u-navbar title" " :bgColor"bgColor"><view class"u-nav-slot" slot"left"><view class"leftCon"><view class"countDown…

Map相关面试答案(都是简单版

一、HashMap的扩容机制 在1.7中 1、先生成新数组 2、遍历老数组中每个位置上的链表的所有节点 3、取每个节点的key&#xff0c;计算出每个元素在新数组中的下标&#xff0c;并添加到新数组中&#xff0c;这里用的是头插法。 4、转移完毕后将新数组复制给hashMap的table属性 1…

Android OpenCV 概述、优缺点及应用场景分析

摘要 随着移动设备性能的不断提升&#xff0c;安卓平台上的计算机视觉应用日益增多。OpenCV作为一个开源的计算机视觉库&#xff0c;在安卓开发中扮演了重要角色。本文将对安卓OpenCV进行概述&#xff0c;并深入探讨其优缺点以及在不同场景下的应用&#xff0c;为开发者提供参考…

u盘为什么一插上电脑就蓝屏,u盘一插电脑就蓝屏

u盘之前还好好的&#xff0c;可以传输文件&#xff0c;使用正常&#xff0c;但是最近使用时却出现问题了。只要将u盘一插入电脑&#xff0c;电脑就显示蓝屏。u盘为什么一插上电脑就蓝屏呢?一般&#xff0c;导致的原因有以下几种。一&#xff0c;主板的SATA或IDE控制器驱动损坏…

Unable to open connection to “Oracle, Microsoft provider V1.0.5000.0“

这是使用vs2018运行vb.net项目时遇到的问题&#xff0c;Navicat可以正常连接oracle但是项目启动却连接不了数据库报了这个错&#xff0c;网上找了很久&#xff0c;没有找到解决方案&#xff0c;最后发现是少了个东西叫 Oracle Client for Microsoft Tools 19c 官网下载地址&a…

VUE中如何使用 JavaScript 变量来设置CSS变量,从而修改CSS属性 --color 的值

// Vue 组件&#xff08;组件名称&#xff1a;DataView&#xff09; <template><div :style"{ --mycolor: color }">这是一个带有自定义颜色的元素</div> </template><script setup lang"ts">const props defineProps({ tit…

C语言处理文本模板:格式信函编程

开篇 本篇文章的问题来源为《编程珠玑》第3章其中一个问题&#xff0c;格式信函编程。说白了就是先在文件中定义一个文本模版&#xff0c;然后使用数据库中的数据去填充这个模版&#xff0c;最后得到填充后的文本&#xff0c;并输出。 问题概要 在常去的网店键入你的名字和密码…