Ring-Buffer,数据高速公路上的环形高架,特别适合 UART、SPI、传感器数据采集

 

你有没有遇到这样的痛点:串口接收一溜数据来了又停,突然一波又爆发,处理起来又卡又乱?Ring-Buffer(环形缓冲区)就是为了解决这种“数据流水线”设计的。下面聊聊它是啥、能干嘛、怎么用,还有优缺点一锅端。

搞清 Ring-Buffer 是啥

Ring-Buffer,本质上就是一段固定大小的数组,首尾连成一个圈。写指针往里丢,读指针往外取,永远不会跑到数组边界越界——因为你把它想象成一个环。圈大小必须是 2 的幂(比如 8、16、32、64……),可存放的最大数据量是 buf_size – 1 字节。

基本初始化很简单:

#include "ringbuffer.h"

char buff[64];
ring_buffer_t ring_buffer;
ring_buffer_init(&ring_buffer, buff, sizeof(buff));  // 最多存 63 字节

它为啥这么牛

  • • 低开销:只靠两个指针和数组,不用 malloc、free。

  • • 高性能:插入/取出都是 O(1) 操作,内存布局连续,Cache Friendly。

  • • 不会碎片:写满会自动覆盖最老数据(可选),或者拒绝写入。

总结一句话:用最小的代价,玩转数据缓存。

怎么安装和上手

  1. 1. 把源码(ringbuffer.h + ringbuffer.c)拷到项目里。

  2. 2. 在你的 Makefile/CMakeLists 里加上这两个文件。

  3. 3. #include "ringbuffer.h",写一段测试代码,跑起来就好了。

// 例子:简单队列 + 批量读取
ring_buffer_queue(&ring_buffer, 'A');
ring_buffer_queue_arr(&ring_buffer, "HELLO", 5);

char ch;
if (!ring_buffer_is_empty(&ring_buffer)) {
    ring_buffer_dequeue(&ring_buffer, &ch);
    // 处理 ch
}

char buf[10];
ring_buffer_size_t n = ring_buffer_dequeue_arr(&ring_buffer, buf, sizeof(buf));
// buf[0..n-1] 就是取出的数据

API 大集合 & 使用示例

函数

功能

ring_buffer_queue

入队 1 字节

ring_buffer_queue_arr

入队多字节

ring_buffer_dequeue

出队 1 字节

ring_buffer_dequeue_arr

出队多字节

ring_buffer_peek

不取出,查看 index 处的数据

ring_buffer_is_empty / _is_full

检查空/满

ring_buffer_num_items

当前存了多少字节

场景一:串口接收

void UART_IRQHandler(void) {
    char d = UART_ReadByte();
    ring_buffer_queue(&ring_buffer, d);
}

场景二:主循环取数据

while (!ring_buffer_is_empty(&ring_buffer)) {
    ring_buffer_dequeue(&ring_buffer, &d);
    process(d);
}

优缺点一目了然

优点

缺点

零动态分配、无碎片

大小固定,不灵活

操作简单、速度快

必须是 2 的幂,使用前要算好

支持批量读写、peek 功能

写满后默认拒绝或覆盖,需根据需求选策略

代码体积小,易移植

不适合超大数据缓存、复杂并发场景

最后来点总结

Ring-Buffer 就像高速公路上的环形高架,你往里放车,堵不死自己也不怕越界。特别适合 UART、SPI、传感器数据采集,多任务间的数据交换,甚至简单的音视频缓存。它的灵活性来自“只要你给我 2ⁿ 大小,我就吃定你”,但是你也要承担“缓冲范围有限”的责任。总的来说,入门极快,效率杠杠的,够用又够爽。

项目地址:https://github.com/AndersKaloer/Ring-Buffer

 

发表评论