环形缓冲区
什么是环形缓冲区
环形缓冲区,也称为循环缓冲区或环形队列,是一种特殊的FIFO(先进先出)数据结构。它使用一块固定大小的内存空间来缓存数据,并通过两个指针(读指针和写指针)来管理数据的读写。当任意一个指针到达缓冲区末尾时,会自动回绕到缓冲区开头,形成一个"环"。
环形缓冲区的用途
- 串口通信
在嵌入式设备中,串口是常用的通信接口。环形缓冲区可用于缓存收发数据,平衡通信速率差异。 - 音视频数据处理
音视频数据往往是连续的数据流。使用环形缓冲区可以平滑数据的生成和消耗,避免数据丢失或延迟。 - 传感器数据采集
传感器数据通常以固定频率采样。环形缓冲区可作为数据采集和处理之间的缓冲,降低实时性要求。 - 多线程数据传递
在多线程编程中,环形缓冲区是一种简单高效的线程间通信方式,无需复杂的同步操作。 - 数据打包与解析
一些通信协议使用特定的数据帧格式。环形缓冲区可用于数据的打包和解析,保证数据的完整性。
环形缓冲区的实现示例
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>// 环形缓冲区大小
#define BUFFER_SIZE 256// 定义解析器状态
typedef enum {STATE_WAIT_START, // 等待消息开始STATE_READ_LENGTH, // 读取消息长度STATE_READ_DATA // 读取消息数据
} ParserState;typedef struct {uint8_t buffer[BUFFER_SIZE];volatile uint16_t head;volatile uint16_t tail;
} RingBuffer;void RingBuffer_Init(RingBuffer *rb) {rb->head = 0;rb->tail = 0;
}bool RingBuffer_Write(RingBuffer *rb, uint8_t data) {uint16_t next = (rb->head + 1) % BUFFER_SIZE;if (next == rb->tail) {// 缓冲区满return false;}rb->buffer[rb->head] = data;rb->head = next;return true;
}bool RingBuffer_Read(RingBuffer *rb, uint8_t *data) {if (rb->head == rb->tail) {// 判满return false;}*data = rb->buffer[rb->tail];rb->tail = (rb->tail + 1) % BUFFER_SIZE;return true;
}// 处理完整消息的函数
void process_message(const uint8_t *msg, uint8_t length) {for (uint8_t i = 0; i < length; i++) {printf("%c", msg[i]);}printf("\n");
}// 有限状态机解析器
void parse_messages(RingBuffer *rb) {static ParserState state = STATE_WAIT_START;static uint8_t msg_length = 0;static uint8_t msg_index = 0;static uint8_t message[128]; //uint8_t byte;while (RingBuffer_Read(rb, &byte)) {switch (state) {case STATE_WAIT_START: // 等待消息起始if (byte == 0xAA) { // 0xAA是消息起始标志state = STATE_READ_LENGTH;}break;case STATE_READ_LENGTH: // 读取消息长度msg_length = byte;if (msg_length > 0 && msg_length < sizeof(message)) {msg_index = 0;state = STATE_READ_DATA;} else {// 无效长度,重置状态state = STATE_WAIT_START;}break;case STATE_READ_DATA: // 读取消息数据message[msg_index++] = byte;if (msg_index >= msg_length) {process_message(message, msg_length);state = STATE_WAIT_START;}break;default:state = STATE_WAIT_START;break;}}
}// 发送函数
void threadA_send(RingBuffer *rb, const uint8_t *msg, uint8_t length) {RingBuffer_Write(rb, 0xAA); // 起始标志RingBuffer_Write(rb, length); // 长度字段for (uint8_t i = 0; i < length; i++) {RingBuffer_Write(rb, msg[i]);}
}// 接收函数
void threadB_receive(RingBuffer *rb) {parse_messages(rb);
}void test_ring_buffer(void) {// 1. 初始化环形缓冲区RingBuffer rb;RingBuffer_Init(&rb);printf("=== 环形缓冲区测试开始 ===\n\n");// 2. 测试基本消息发送和接收printf("测试1: 基本消息收发\n");const uint8_t test_msg1[] = "Hello World";threadA_send(&rb, test_msg1, sizeof(test_msg1) - 1);threadB_receive(&rb);// 3. 测试空缓冲区printf("\n测试2: 空缓冲区读取\n");uint8_t temp;if (!RingBuffer_Read(&rb, &temp)) {printf("空缓冲区测试通过: 无法从空缓冲区读取数据\n");}// 4. 测试缓冲区满状态printf("\n测试3: 缓冲区满状态\n");uint8_t large_msg[BUFFER_SIZE];for (int i = 0; i < BUFFER_SIZE; i++) {large_msg[i] = 'A' + (i % 26); // 填充A-Z循环}bool write_result = true;int write_count = 0;while (write_result && write_count < BUFFER_SIZE + 10) {write_result = RingBuffer_Write(&rb, large_msg[write_count % BUFFER_SIZE]);write_count++;}printf("写入计数: %d (应小于缓冲区大小)\n", write_count - 1);// 5. 测试长消息分段发送printf("\n测试4: 长消息分段发送\n");RingBuffer_Init(&rb); // 重新初始化const uint8_t long_msg[] = "This is a long message to test multiple segments";const int SEGMENT_SIZE = 10;for (size_t i = 0; i < (size_t)(sizeof(long_msg) - 1); i += (size_t)SEGMENT_SIZE) {size_t current_length = ((sizeof(long_msg) - 1 - i) < (size_t)SEGMENT_SIZE) ? (sizeof(long_msg) - 1 - i) : (size_t)SEGMENT_SIZE;threadA_send(&rb, &long_msg[i], current_length);threadB_receive(&rb);}// 6. 测试无效消息printf("\n测试5: 无效消息处理\n");uint8_t invalid_msg[] = {0xAA, 0xFF, 0x01, 0x02}; // 无效长度for (size_t i = 0; i < sizeof(invalid_msg); i++) {RingBuffer_Write(&rb, invalid_msg[i]);}threadB_receive(&rb);// 7. 测试快速读写切换printf("\n测试6: 快速读写切换\n");const uint8_t test_msg2[] = "Test";for (int i = 0; i < 5; i++) {threadA_send(&rb, test_msg2, sizeof(test_msg2) - 1);threadB_receive(&rb);}printf("\n=== 环形缓冲区测试完成 ===\n");
}int main() {test_ring_buffer();return 0;
}
总结
与传统的数组或链表相比,环形缓冲区有以下优点:
1.无需频繁移动数据。环形缓冲区的读写指针移动不会导致数据搬移,效率更高。
2.自动处理缓冲区"满"和"空"的状态。通过读写指针的关系可以判断缓冲区状态,无需额外的计数器。
3. 适用于生产者-消费者模型。一个线程写入数据,另一个线程读取数据,天然支持异步处理。
相关文章:
环形缓冲区
什么是环形缓冲区 环形缓冲区,也称为循环缓冲区或环形队列,是一种特殊的FIFO(先进先出)数据结构。它使用一块固定大小的内存空间来缓存数据,并通过两个指针(读指针和写指针)来管理数据的读写。当任意一个指针到达缓冲区末尾时,会自动回绕到缓冲区开头,形成一个"环"。…...
jQuery-Word-Export 使用记录及完整修正文件下载 jquery.wordexport.js
参考资料: jQuery-Word-Export导出word_jquery.wordexport.js下载-CSDN博客 近期又需要自己做个 Html2Doc 的解决方案,因为客户又不想要 Html2pdf 的下载了,当初还给我费尽心思解决Html转pdf时中文输出的问题(html转pdf文件下载之…...
云服务器部署WebSocket项目
WebSocket是一种在单个TCP连接上进行全双工通信的协议,其设计的目的是在Web浏览器和Web服务器之间进行实时通信(实时Web) WebSocket协议的优点包括: 1. 更高效的网络利用率:与HTTP相比,WebSocket的握手只…...
C#+数据库 实现动态权限设置
将权限信息存储在数据库中,支持动态调整。根据用户所属的角色、特定的功能模块,动态加载权限” 1. 数据库设计 根据这种需求,可以通过以下表设计: 用户表 (Users):存储用户信息。角色表 (Roles):存储角色…...
(原创)Android Studio新老界面UI切换及老版本下载地址
前言 这两天下载了一个新版的Android Studio,发现整个界面都发生了很大改动: 新的界面的一些设置可参考一些博客: Android Studio新版UI常用设置 但是对于一些急着开发的小伙伴来说,没有时间去适应,那么怎么办呢&am…...
Ubuntu24虚拟机-gnome-boxes
推荐使用gnome-boxes, virtualbox构建失败,multipass需要开启防火墙 sudo apt install gnome-boxes创建完毕~...
k8s rainbond centos7/win10 -20241124
参考 https://www.rainbond.com/ 国内一站式云原生平台 对centos7环境支持不太行 [lighthouseVM-16-5-centos ~]$ curl -o install.sh https://get.rainbond.com && bash ./install.sh 2024-11-24 09:56:57 ERROR: Ops! Docker daemon is not running. Start docke…...
SpringBoot+Vue滑雪社区网站设计与实现
【1】系统介绍 研究背景 随着互联网技术的快速发展和冰雪运动的普及,滑雪作为一种受欢迎的冬季运动项目,吸引了越来越多的爱好者。与此同时,社交媒体和在线社区平台的兴起为滑雪爱好者提供了一个交流经验、分享心得、获取信息的重要渠道。滑…...
MySql.2
sql查询语句执行过程 SQL 查询语句的执行过程是一个复杂的过程,涉及多个步骤。以下是典型的关系数据库管理系统 (RDBMS) 中 SQL 查询语句的执行过程概述: 1. 客户端发送查询 用户通过 SQL 客户端或应用程序发送 SQL 查询语句给数据库服务器。 2. …...
算法之区间和题目讲解
题干 难度:简单 题目分析 题目要求算出每个指定区间内元素的总和。 然而,区间在输入的最下面,所以按照暴力破解的思路,我们首先要遍历数组,把它的值都存进去。 然后,遍历下面的区间,从索引a…...
价格分类(神经网络)
# 1.导入依赖包 import timeimport torch import torch.nn as nn import torch.optim as optimfrom torch.utils.data import TensorDataset, DataLoader from sklearn.model_selection import train_test_splitimport numpy as np import pandas as pd import matplotlib.pypl…...
对智能电视直播App的恶意监控
首先我们要指出中国广电总局推出的一个政策性文件是恶意监控的始作俑者,这个广电总局的政策性文件禁止智能电视和电视盒子安装直播软件。应该说这个政策性文件是为了保护特殊利益集团,阻挠技术进步和发展的。 有那么一些电视机和电视盒子的厂商和电信运…...
【JavaEE初阶】多线程初阶下部
文章目录 前言一、volatile关键字volatile 能保证内存可见性 二、wait 和 notify2.1 wait()方法2.2 notify()方法2.3 notifyAll()方法2.4 wait 和 sleep 的对比(面试题) 三、多线程案例单例模式 四、总结-保证线程安全的思路五、对比线程和进程总结 前言…...
macOS上进行Ant Design Pro实战教程(一)
由于一个AI项目的前端使用了umi,本教程根据阿里官网上的 《Ant Design 实战教程(beta 版)》来实操一下,我使用macOS操作系统,VS Code 开发环境。 一、开发环境 1、安装nodejs, npm, yarn 官网上建议使用cnpm…...
智能合约运行原理
点个关注吧!! 用一句话来总结,智能合约就像是一个自动售货机:你投入硬币(触发条件),选择商品(执行合约),然后机器就会自动给你商品(执行结果&…...
安卓动态添加View
在安卓应用中,有很多时候需要动态添加View。比如从后台获取商品列表,根据商品数量在页面渲染对应数量的条目,这时候就需要动态添加View。 1.动态添加View的方法 动态添加View有两种方法: 由代码生成子View:这种方式…...
前端预览pdf文件流
需求 后端接口返回pdf文件流,实现新窗口预览pdf。 解决方案 把后端返回的pdf文件流转为blob路径,利用浏览器直接预览。 具体实现步骤 1、引入axios import axios from axios;2、创建预览方法(具体使用时将axios的请求路径替换为你的后端…...
【测试工具JMeter篇】JMeter性能测试入门级教程(一)出炉,测试君请各位收藏了!!!
一、前言 Apache JMeter是纯Java的开源软件,最初由Apache软件基金会的Stefano Mazzocchi开发,旨在加载测试功能行为和测量性能。可以使用JMeter进行性能测试,即针对重负载、多用户和并发流量测试Web应用程序。 我们选择JMeter原因 是否测试过…...
【zookeeper03】消息队列与微服务之zookeeper集群部署
ZooKeeper 集群部署 1.ZooKeeper 集群介绍 ZooKeeper集群用于解决单点和单机性能及数据高可用等问题。 集群结构 Zookeeper集群基于Master/Slave的模型 处于主要地位负责处理写操作)的主机称为Leader节点,处于次要地位主要负责处理读操作的主机称为 follower 节点…...
从 Llama 1 到 3.1:Llama 模型架构演进详解
编者按: 面对 Llama 模型家族的持续更新,您是否想要了解它们之间的关键区别和实际性能表现?本文将探讨 Llama 系列模型的架构演变,梳理了 Llama 模型从 1.0 到 3.1 的完整演进历程,深入剖析了每个版本的技术创新&#…...
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...
从实验室到产业:IndexTTS 在六大核心场景的落地实践
一、内容创作:重构数字内容生产范式 在短视频创作领域,IndexTTS 的语音克隆技术彻底改变了配音流程。B 站 UP 主通过 5 秒参考音频即可克隆出郭老师音色,生成的 “各位吴彦祖们大家好” 语音相似度达 97%,单条视频播放量突破百万…...
