Ring-Buffer API完全参考手册:9个函数搞定嵌入式数据缓冲全场景

📅 2026/8/21 18:58:25
Ring-Buffer API完全参考手册:9个函数搞定嵌入式数据缓冲全场景
Ring-Buffer API完全参考手册9个函数搞定嵌入式数据缓冲全场景【免费下载链接】Ring-BufferA simple ring buffer (circular buffer) designed for embedded systems.项目地址: https://gitcode.com/gh_mirrors/rin/Ring-BufferRing-Buffer 是一个专为嵌入式系统设计的极简环形缓冲区Circular Buffer开源库。整套 Ring-Buffer API 只有 9 个函数一个头文件 ringbuffer.h、一个实现文件 ringbuffer.c却能覆盖数据缓冲的写入、读取、预览与状态查询全部场景。本文是一份面向新手的 Ring-Buffer API 完全参考手册将逐一讲解 9 个函数的原型、参数、返回值与典型用法并附带嵌入式实战示例帮助你用最小的代码量解决嵌入式数据缓冲问题。什么是环形缓冲区嵌入式开发为什么离不开它环形缓冲区Ring Buffer / Circular Buffer是一种固定容量的 FIFO先进先出数据结构数据按顺序写入按顺序读出读写指针到达末尾后自动“绕回”起点就像一圈首尾相连的跑道。嵌入式开发中它几乎是标配原因很实在无内存拷贝写入和读取都只移动下标不搬移数据速度极快天然解耦中断里写、主循环里读生产者与消费者互不阻塞零动态内存内存由调用方提供不依赖 malloc适合裸机环境预测开销时间复杂度恒为 O(1)实时性可控。内存数组8 字节 ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐ │ b0 │ b1 │ b2 │ b3 │ b4 │ b5 │ b6 │ b7 │ └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ ▲ ▲ tail_index下次读取位置 head_index下次写入位置Ring-Buffer 项目速览两个文件组成的轻量级环形缓冲区库Ring-Buffer 由 Anders Kalør 编写整个工程只有两个核心源文件非常适合直接放进嵌入式工程文件作用ringbuffer.h结构体定义与 9 个函数的声明含注释文档ringbuffer.c9 个函数的具体实现examples/simple.c全功能演示写入、peek、批量读写、溢出覆盖examples/tail.c用环形缓冲区实现 Unixtail -c 15命令核心设计特点✅ 纯 C 实现C99 标准并通过extern C兼容 C 工程✅ 缓冲区大小必须是2 的幂最多存放buf_size - 1个字节✅ 用位与运算 (size-1)代替取模没有除法运算对没有硬件除法器的 MCU 非常友好✅ 缓冲区写满后自动覆盖最旧数据不会越界✅ 头文件中is_empty、is_full、num_items均为inline实现零调用开销。9 个函数总览一张表看懂 Ring-Buffer API在深入细节之前先用一张表建立整体印象。全部 9 个函数按功能分为四类#函数分类一句话说明1ring_buffer_init初始化初始化或清空缓冲区2ring_buffer_queue写入入队单个字节满时覆盖最旧数据3ring_buffer_queue_arr写入批量入队字节数组4ring_buffer_dequeue读取出队最旧的字节5ring_buffer_dequeue_arr读取批量出队字节6ring_buffer_peek查看预览指定位置字节不删除7ring_buffer_is_empty状态缓冲区是否为空8ring_buffer_is_full状态缓冲区是否已满9ring_buffer_num_items状态当前元素个数快速上手环形缓冲区初始化与编译方法获取源码并编译运行示例git clone https://gitcode.com/gh_mirrors/rin/Ring-Buffer cd Ring-Buffer/examples make simple ./simple也可以手动编译对应 examples/Makefile 中的规则gcc -Wall -O2 -stdc99 -o simple simple.c ../ringbuffer.cring_buffer_init初始化或复位环形缓冲区void ring_buffer_init(ring_buffer_t *buffer, char *buf, size_t buf_size);char buff[64]; ring_buffer_t rb; ring_buffer_init(rb, buff, sizeof(buff)); /* 64 字节内存最多存 63 字节 */要点内部通过断言检查buf_size是否为 2 的幂不满足时程序直接报错函数内部会把读写下标清零因此随时再次调用即可复位缓冲区结构体ring_buffer_t包含buffer内存指针、buffer_mask掩码、tail_index与head_index四个字段源码见 ringbuffer.h。环形缓冲区写入 API单字节与批量入队函数ring_buffer_queue写入单个字节void ring_buffer_queue(ring_buffer_t *buffer, char data);这是最核心的入队函数非常适合在串口/UART/SPI 中断中逐字节接收数据。注意它的覆盖策略当缓冲区已满时会自动推进 tail 下标覆盖最旧的字节保证永远不越界。如果你不希望覆盖数据写入前先用ring_buffer_is_full检查一下。ring_buffer_queue_arr批量写入字节数组void ring_buffer_queue_arr(ring_buffer_t *buffer, const char *data, ring_buffer_size_t size);底层是循环调用ring_buffer_queue语义完全相同。适合一次性把一帧协议数据如 AT 指令、传感器报文整体压入缓冲区ring_buffer_queue_arr(rb, Hello, Ring Buffer!, 20);环形缓冲区读取 API出队与批量读取函数ring_buffer_dequeue取出最旧字节uint8_t ring_buffer_dequeue(ring_buffer_t *buffer, char *data);读取tail指向的最旧字节成功后 tail 前进缓冲区为空时返回 0成功返回 1。这是消费端最常用的函数典型的“读一个处理一个”模式char tmp; while (ring_buffer_dequeue(rb, tmp)) { /* 处理字节 tmp */ }ring_buffer_dequeue_arr批量取出字节ring_buffer_size_t ring_buffer_dequeue_arr(ring_buffer_t *buffer, char *data, ring_buffer_size_t len);一次最多取出len个字节返回实际取出的字节数缓冲区不足时可能小于len。适合按固定帧长批量消费数据例如每次取 13 字节char frame[13]; if (ring_buffer_dequeue_arr(rb, frame, 13) 13) { /* 拿到一帧完整数据 */ }环形缓冲区查看 APIpeek 预览不删除ring_buffer_peek按索引查看数据uint8_t ring_buffer_peek(ring_buffer_t *buffer, char *data, ring_buffer_size_t index);与出队不同peek 只“看一眼”而不移动任何指针数据仍留在缓冲区中。index从 0 开始0 表示最旧的元素。典型场景是先预览头部数据判断“这帧要不要收”再决定是否真正出队。越界index 超出元素数时返回 0。环形缓冲区状态查询 API空、满与元素数量这三个函数都是inline实现且只做位运算几乎没有性能开销函数返回 1 的条件说明ring_buffer_is_emptyhead tail缓冲区为空ring_buffer_is_full(head - tail) mask mask缓冲区已满ring_buffer_num_items——返回当前元素个数if (ring_buffer_is_full(rb)) { printf(Buffer is full, contains %d bytes\n, ring_buffer_num_items(rb)); }空/满判断的依据是读写下标是否相等这也是容量是buf_size - 1的原因——必须留出一个位置来区分“空”和“满”。嵌入式实战用 3 个函数实现 tail -c 15 命令examples/tail.c 只用了ring_buffer_init、ring_buffer_queue、ring_buffer_dequeue三个函数就实现了 Unixtail -c 15的效果——输出输入流的最后 15 个字节char data[16]; struct ring_buffer_t buffer; ring_buffer_init(buffer, data, sizeof(data)); while ((c getchar()) ! EOF) ring_buffer_queue(buffer, c); /* 满了自动覆盖最旧字节 */ while (ring_buffer_dequeue(buffer, d)) putchar(d); /* 留下的恰好是最后 15 字节 */原理正是“写满覆盖最旧数据”16 字节内存、最多存 15 字节数据不断压入最旧的被持续挤掉结束时缓冲区里恰好保留最后 15 个字节。短短几行代码就把环形缓冲区最实用的特性演示得淋漓尽致。嵌入式使用注意事项2 的幂大小与覆盖策略最后总结嵌入式环境下使用 Ring-Buffer 的几条关键经验大小必须是 2 的幂64、128、256…… 否则ring_buffer_init的断言会直接失败实际容量 buf_size - 1申请 64 字节只能放 63 个字节规划内存时留好余量覆盖语义要心里有数queue写满后静默覆盖旧数据接收关键数据时先查ring_buffer_is_full单生产者单消费者模型库本身不加锁典型用法是中断写、主循环读多线程场景需要自行加临界区保护零动态分配内存由用户提供可以放在静态区或直接声明为全局数组非常适合资源受限的 MCU 项目。总结9 个函数如何选择如果你刚开始接触环形缓冲区只需记住这条主线ring_buffer_init建缓冲区 →ring_buffer_queue/ring_buffer_queue_arr写入 →ring_buffer_dequeue/ring_buffer_dequeue_arr读取 →ring_buffer_is_empty/ring_buffer_is_full/ring_buffer_num_items随时掌握状态 →ring_buffer_peek预览数据。Ring-Buffer 用 9 个函数、约 150 行代码把嵌入式数据缓冲的常见场景全部覆盖。无论是串口收发缓存、协议解析缓冲还是命令历史记录这套轻量级 Ring-Buffer API 都是值得放进你工具箱的实用选择。【免费下载链接】Ring-BufferA simple ring buffer (circular buffer) designed for embedded systems.项目地址: https://gitcode.com/gh_mirrors/rin/Ring-Buffer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考