当前位置: 首页 > news >正文

环形缓冲区(ring buffer)

推荐参考        环形缓冲区(ring buffer)原理与实现详解

环形缓冲区(ring buffer)的概念

环形缓冲区(又称循环缓冲区、圆形队列或圆形缓冲区)是一种具有先进先出(FIFO)特性的高效缓冲区结构。尽管名称中包含"环形"概念,但实际实现上它仍是一段线性内存空间,只是通过巧妙的逻辑处理使其具有首尾相连的循环特性。

这种设计具有以下几个关键特点:

内存效率:使用固定大小的数组实现,避免了动态内存分配的开销

读写指针:通过维护读指针(head)和写指针(tail)两个索引来跟踪缓冲区状态

循环机制:当指针到达数组末尾时会自动绕回到数组开头,形成逻辑上的环形结构

线程安全:通过互斥锁或原子操作确保多线程/多进程环境下的安全访问

环形缓冲区(ring buffer)的常见应用场景

数据流处理: 环形缓冲区在实时数据流处理中非常有用,如音频/视频流处理、传感器数据采集。生产者持续写入数据,消费者按顺序读取,避免数据丢失或阻塞。

生产者-消费者模型: 在多线程编程中,环形缓冲区作为共享数据结构,协调生产者和消费者的速度差异。生产者写入数据到缓冲区,消费者从中读取,无需频繁同步。

网络数据包处理 :网络设备(如路由器、交换机)使用环形缓冲区存储临时数据包。高速网络接口卡(NIC)通常采用环形缓冲区管理接收和发送的数据包队列。

嵌入式系统 :资源受限的嵌入式系统中,环形缓冲区因其固定大小和高效的内存利用率被广泛使用。例如串口通信中存储接收到的字节流。

日志系统 :高性能日志系统利用环形缓冲区存储临时日志条目。当缓冲区满时,旧日志被新日志覆盖,确保最新日志始终可用。

图形渲染 :GPU渲染管线中,环形缓冲区用于存储顶点数据或命令队列。图形API(如Vulkan)常使用环形缓冲区管理帧数据

环形缓冲区(ring buffer)实现原理

环形缓冲区(ring buffer)数据结构
  1. 保存环形缓冲区的首地址信息的指针buffer
  2. 保存读取起始位置信息的变量head
  3. 保存写入起始位置信息的变量tail
  4. 保存当前可读的数据量信息size
  5. 保存缓冲区容量大小信息的capacity
typedef struct {char *buffer;  // 数据存储区int head;      // 读取位置int tail;      // 写入位置int size;      // 当前数据量int capacity;  // 缓冲区容量
} loopbuf_t;
环形缓冲区(ring buffer)算法实现
  • init,分配并初始化环形缓冲区
  • free,销毁环形缓冲区
  • write,写n个数据进环形缓冲区
  • read,从环形缓冲区中读出n个数据
extern loopbuf_t* loopbuf_init(int capacity);
extern void loopbuf_free(loopbuf_t *lb);
extern int loopbuf_write(loopbuf_t *lb, const char *data, int n);
extern int loopbuf_read(loopbuf_t *lb, char *data, int n);
分配并初始化环形缓冲区

给结构体分配内存使用malloc分配的是堆内存只要不释放就会全局存在

使用malloc通过缓冲区容量大小来分配缓冲区长度,全局存在

初始化缓冲区变量信息

// (1) 初始化环形缓冲区
loopbuf_t* loopbuf_init(int capacity) {if (capacity <= 0) return NULL;loopbuf_t *lb = (loopbuf_t*)malloc(sizeof(loopbuf_t));//给结构体对象分配内存if (!lb) return NULL;//如果分配失败就返回NULLlb->buffer = (char*)malloc(capacity * sizeof(char));//给缓冲区分配容量if (!lb->buffer) {		//如果分配失败就释放返回NULLfree(lb);return NULL;}lb->head = 0;lb->tail = 0;lb->size = 0;lb->capacity = capacity;return lb;
}
销毁环形缓冲区

不再使用缓冲区需要释放内存,防止内存泄漏

// (2) 销毁环形缓冲区
void loopbuf_free(loopbuf_t *lb) {if (lb) {if (lb->buffer) {free(lb->buffer);}free(lb);}
}
写n个数据进环形缓冲区

1、写入缓冲区需要判断是否覆盖旧数据

通过判断当前需要写入的数据是否大于剩余容量,如果大于则进入覆盖逻辑,小于则进入直接拷贝逻辑

2、覆盖逻辑

更新保存读取起始位置信息的变量head,保证其保存的信息是存在时间最长的数据

更新当前数据量。

分段写入:先写入剩余容量,再写入覆盖部分

// (3) 写入n个数据到缓冲区
int loopbuf_write(loopbuf_t *lb, const char *data, int n) {if (!lb || !data || n <= 0) return 0;// 如果需要覆盖旧数据if (n > lb->capacity - lb->size) {//判断缓冲区剩余容量是否可以完全写入,不完全则覆盖旧数据//需要覆盖的旧数据数量。如果 n大于可用空间,多出的部分会覆盖缓冲区中最旧的的数据。int overwrite_count = n - (lb->capacity - lb->size);//lb->head指向被覆盖部分的后一位(保证读取的第一个数据存在时间最长),取余确保指针在缓冲区范围内循环,维持环形结构lb->head = (lb->head + overwrite_count) % lb->capacity;lb->size = lb->capacity;//当前数据量} else {lb->size += n;//当前数据量}// 分段写入数据int write_pos = lb->tail;//写入数据起始位if (write_pos + n <= lb->capacity) {//判断缓冲区容量是否足够保存旧数据和新数据memcpy(lb->buffer + write_pos, data, n);//缓冲区足够则直接写入} else {int first_part = lb->capacity - write_pos;//剩余容量memcpy(lb->buffer + write_pos, data, first_part);//写入剩余容量memcpy(lb->buffer, data + first_part, n - first_part);//覆盖旧数据}lb->tail = (lb->tail + n) % lb->capacity;//更新写入位置return n;
}
从环形缓冲区中读出n个数据

1、判断实际读取数量

如果需要读取数据大于当前数据量则实际读取数据为当前数据量,如果小于当前数据量,则实际读取数据为需要读取的数据量

2、判断是否需要存在越界读取的数据

如果存在越界读取的数据则进入,越界读取数据逻辑,通过判断起始位置序号加上实际读取数据量,是否大于缓冲区容量

3、更新数据量和读取起始位

head读取起始位一定保存着最旧的数据位置序号

当前数据量减去实际读取数据量,保证以读取的数据在逻辑上失效

// (4) 从缓冲区读取n个数据,从最老的数据开始读取
int loopbuf_read(loopbuf_t *lb, char *data, int n) {if (!lb || !data || n <= 0 || lb->size == 0) return 0;int actual_read = (n > lb->size) ? lb->size : n;//判断读取长度是否大于当前数据量int read_pos = lb->head;//读取起始位if (read_pos + actual_read <= lb->capacity) {//判断读取数据是否越界memcpy(data, lb->buffer + read_pos, actual_read);//如果读取读取数据里不存在越界则正常读取} else {int first_part = lb->capacity - read_pos; //没有越界的部分长度memcpy(data, lb->buffer + read_pos, first_part);memcpy(data + first_part, lb->buffer, actual_read - first_part);//读取越界部分}lb->head = (lb->head + actual_read) % lb->capacity;//更新读取位置信息,序号指向存在时间最长的数据lb->size -= actual_read;//更新当前数据return actual_read;
}

环形缓冲区(ring buffer)测试

void loopbuf_test(void)
{// 测试基本功能loopbuf_t *buf = loopbuf_init(5);printf("=== 基本功能测试 ===\r\n");// 写入数据loopbuf_write(buf, "12345", 5);printf("写入: 12345\r\n");// 读取数据char read_data[10] = {0};loopbuf_read(buf, read_data, 5);printf("读取: %s\r\n", read_data);// 测试覆盖写入printf("=== 覆盖写入测试 ===\r\n");loopbuf_write(buf, "ABCDE", 5);loopbuf_write(buf, "FGHI", 4);memset(read_data, 0, sizeof(read_data));loopbuf_read(buf, read_data, 5);//第一个数据指向最旧的数据就是E	然后是FGHIprintf("覆盖后读取: %s\r\n", read_data);// 测试部分读写printf("=== 部分读写测试 ===\r\n");loopbuf_write(buf, "123", 3);printf("写入3字节后,读取2字节: ");memset(read_data, 0, sizeof(read_data));loopbuf_read(buf, read_data, 2);printf("%s\r\n", read_data);loopbuf_free(buf);}

http://www.dtcms.com/a/554721.html

相关文章:

  • 网站服务器租用方法wordpress 下载短代码
  • 零基础能考信创认证吗?报考条件是什么?
  • 免费制作微信小程序的网站企业购
  • 计算机网络技专业术网站开发张家口住房和城乡建设部网站
  • Gartner发布AI-ITSM最新趋势!
  • Vue3 异步组件(懒加载组件)
  • 如何做电影网站才不侵权贵州省省建设厅网站
  • osgearth\AFsim如何加载影像瓦片数据和高程数据
  • 是做网站设计好还是杂志美编好有没有傻瓜式建设网站
  • Derby - Derby 服务器(Derby 概述、Derby 服务器下载与启动、Derby 连接数据库与创建数据表、Derby 数据库操作)
  • 慈溪高端网站设计甘肃嘉峪关建设局网站
  • 重庆渝云建设有限公司官方网站深圳网站运营
  • 【开题答辩实录分享】以《自动售货机刷脸支付系统的设计与实现》为例进行答辩实录分享
  • 瑜伽 网站模板夏津网站建设电话
  • 长沙手机网站设计公司网站建设与客户价格谈判技巧
  • 网页抓包实战,工具选型、分层排查与真机取证流程
  • 荆门市城乡建设管理局网站广州建设工程中心网站
  • 可以自己建设购物网站家具营销策划方案
  • 基于阿里云效实现cicd记录
  • 网站结构的规划与设计拓者设计吧首页
  • 递归专题4 - 网格DFS与回溯
  • 免费排版网站专业网站是什么意思
  • 精准且快速校准的语音神经假体研究与学习
  • 授权购买网站广州越秀网站制作
  • 马克·扎克伯格大学做的网站lnmp wordpress 500
  • Maven 入门指南
  • 网站建设电话销售技巧和话术合肥网络seo
  • 湖北人工智能建站系统软件360建筑兼职网官网
  • 怎么搭建自己的电影网站建设部网站社保联网
  • 学习笔记二:发展历程