一种基于镜像指示位办法的RingBuffer实现,解决Mirror和2的幂个数限制
简介
在嵌入式开发中,经常有需要用到RingBuffer的概念,在RingBuffer中经常遇到一个Buffer满和Buffer空的判断的问题,一般的做法是留一个单位的buffer不用,这样做最省事,但是当RingBuffer单位是一个结构体时,这个浪费就不能接受了,市面上大多数解决办法是镜像指示位办法,但是具体实现上又有各种设计,但是并不是满足的开发需要,所以有本项目。
本项目地址:bobwenstudy/simple_ringbuffer: 一种基于镜像指示位办法的RingBuffer实现,解决Mirror和2的幂个数限制 (github.com),参考资料有:环形缓冲器 - 维基百科,自由的百科全书 (wikipedia.org),ring buffer,一篇文章讲透它? - 知乎 (zhihu.com)。
对比现有的实现,本项目的特点是。
类别 | simple_ringbuffer | kfifo(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.c和test_1.c和test_2.c:测试例程。
- main.c:测试例程。
- build.mk和Makefile: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!
可以看到,所有涉及到测试都通过。
相关文章:
一种基于镜像指示位办法的RingBuffer实现,解决Mirror和2的幂个数限制
简介 在嵌入式开发中,经常有需要用到RingBuffer的概念,在RingBuffer中经常遇到一个Buffer满和Buffer空的判断的问题,一般的做法是留一个单位的buffer不用,这样做最省事,但是当RingBuffer单位是一个结构体时࿰…...

【Java开发指南 | 第十一篇】Java运算符
读者可订阅专栏:Java开发指南 |【CSDN秋说】 文章目录 算术运算符关系运算符位运算符逻辑运算符赋值运算符条件运算符(?:)instanceof 运算符Java运算符优先级 Java运算符包括:算术运算符、关系运算符、位运算符、逻辑运算符、赋值…...

【IC前端虚拟项目】验证环境方案思路和文档组织
【IC前端虚拟项目】数据搬运指令处理模块前端实现虚拟项目说明-CSDN博客 对于mvu的验证环境,从功能角度就可以分析出需要搭建哪些部分,再看一下mvu的周围环境哈: 很明显验证环境必然要包括几个部分: 1.模拟idu发送指令; 2.模拟ram/ddr读写数据; 3.rm模拟mvu的行为; …...

程序设计|C语言教学——C语言基础1:C语言的引入和入门
一、程序的执行 1.定义 解释:借助一个程序,那个程序能够试图理解你的程序,然后按照你的要求执行。下次执行的时候还需要从零开始解释。 编译:借助一个程序,能够像翻译官一样,把你的程序翻译成机器语言&a…...

初学python记录:力扣928. 尽量减少恶意软件的传播 II
题目: 给定一个由 n 个节点组成的网络,用 n x n 个邻接矩阵 graph 表示。在节点网络中,只有当 graph[i][j] 1 时,节点 i 能够直接连接到另一个节点 j。 一些节点 initial 最初被恶意软件感染。只要两个节点直接连接,…...

LlamaIndex 组件 - Storing
文章目录 一、储存概览1、概念2、使用模式3、模块 二、Vector Stores1、简单向量存储2、矢量存储选项和功能支持3、Example Notebooks 三、文件存储1、简单文档存储2、MongoDB 文档存储3、Redis 文档存储4、Firestore 文档存储 四、索引存储1、简单索引存储2、MongoDB 索引存储…...

在Linux系统中设定延迟任务
一、在系统中设定延迟任务要求如下: 要求: 在系统中建立easylee用户,设定其密码为easylee 延迟任务由root用户建立 要求在5小时后备份系统中的用户信息文件到/backup中 确保延迟任务是使用非交互模式建立 确保系统中只有root用户和easylee用户…...

JVM之方法区的详细解析
方法区 方法区:是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、即时编译器编译后的代码等数据,虽然 Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是也叫 Non-Heap(非堆) 设置方法…...
Go 使用ObjectID
ObjectID介绍 MongoDB中的ObjectId是一种特殊的12字节 BSON 类型数据,用于为主文档提供唯一的标识符,默认情况下作为 _id 字段的默认值出现在每一个MongoDB集合中的文档中。以下是ObjectId的具体组成: 1. 时间戳(Timestamp&…...

基于SpringBoot+Vue的疾病防控系统设计与实现(源码+文档+包运行)
一.系统概述 在如今社会上,关于信息上面的处理,没有任何一个企业或者个人会忽视,如何让信息急速传递,并且归档储存查询,采用之前的纸张记录模式已经不符合当前使用要求了。所以,对疾病防控信息管理的提升&a…...

2024年阿里云4核8G配置云服务器价格低性能高!
阿里云4核8G服务器租用优惠价格700元1年,配置为ECS通用算力型u1实例(ecs.u1-c1m2.xlarge)4核8G配置、1M到3M带宽可选、ESSD Entry系统盘20G到40G可选,CPU采用Intel(R) Xeon(R) Platinum处理器,阿里云优惠 aliyunfuwuqi…...
关于ContentProvider这一遍就够了
ContentProvider是什么? ContentProvider是Android四大组件之一,主要用于不同应用程序之间或者同一个应用程序的不同部分之间共享数据。它是Android系统中用于存储和检索数据的抽象层,允许不同的应用程序通过统一的接口访问数据,…...
《1w实盘and大盘基金预测 day23》
这几天预测错麻了,哈哈哈,完全和技术没关系,全是消息面。 昨日预测: 2958-2984-3010 证券继续下跌,昨天诱多把我诱惑进去了(看2-3天的反弹也没了),今天直接出掉昨天买的。 整体操作…...

向量数据库与图数据库:理解它们的区别
作者:Elastic Platform Team 大数据管理不仅仅是尽可能存储更多的数据。它关乎能够识别有意义的见解、发现隐藏的模式,并做出明智的决策。这种对高级分析的追求一直是数据建模和存储解决方案创新的驱动力,远远超出了传统关系数据库。 这些创…...

WIN7用上最新版Chrome
1.下载WIN10最新版Chrome的离线安装包 谷歌浏览器 Chrome 最新版离线安装包下载地址 v123.0.6312.123 - 每日自动更新 | 异次元软件 文件名称:123.0.6312.123_chrome_installer.exe。 123.0.6312.123_chrome_installer.exe 文件右键解压缩得到 chrome.7z&#x…...

node.jd版本降级/升级
第一步.先清空本地安装的node.js版本 按健winR弹出窗口,键盘输入cmd,然后敲回车(或者鼠标直接点击电脑桌面最左下角的win窗口图标弹出,输入cmd再点击回车键) 进入命令控制行窗口,输入where node,查看本地…...

python+playwright 学习-88 禁止加载图片等资源
前言 对于爬虫的小伙伴来说,有时候只需抓取页面的文本,不用加载图片,可以加快操作页面速度,那么我们可以设置禁止加载图片等资源。 禁止图片加载 根据url地址的后缀,图片资源后缀一般是png,jpg,jpeg,gif等格式。 from playwright.sync_api import sync_playwrightwith…...

Linux:Redis7.2.4的简单在线部署(1)
注意:我写的这个文章是以最快速的办法去搭建一个redis的基础环境,作用是为了做实验简单的练习,如果你想搭建一个相对稳定的redis去使用,可以看我下面这个文章 Linux:Redis7.2.4的源码包部署(2)-…...

HackMyVM-Connection
目录 信息收集 arp nmap WEB web信息收集 dirsearch smbclient put shell 提权 系统信息收集 suid gdb提权 信息收集 arp ┌─[rootparrot]─[~/HackMyVM] └──╼ #arp-scan -l Interface: enp0s3, type: EN10MB, MAC: 08:00:27:16:3d:f8, IPv4: 192.168.9.115 S…...

Prometheus接入AlterManager配置邮件告警(基于K8S环境部署)
目录 一.配置Alertmanager告警发送至邮箱二.Prometheus接入AlertManager三.部署PrometheusAlterManager(放到一个Pod中)四. 测试告警 基于 此环境做实验 一.配置Alertmanager告警发送至邮箱 1.创建AlertManager ConfigMap资源清单 vim alertmanager-cm.yaml --- kind: Confi…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...