环形缓冲区(Circular Buffer),又被称为环形队列或循环缓冲区,是一种常用的数据结构,特别适用于缓冲流数据的场景,如硬件设备的数据交换、网络通信等
1. 什么是环形缓冲区
环形缓冲区是一个先进先出(FIFO)的闭环的存储空间。相当于就是一个固定大小的数组,和两个指针,读指针和写指针。环形 缓冲区的”环形“特性意味着当任一指针到达数组的末端的时候,会自动回绕到数据开始的位置。
2. 环形缓冲区读写指针
- 写指针(head): 指向环形缓冲区下一个数据将要写入的位置 , 当有新的数据写入缓冲区的时候,数据会被存储在写指针指向的位置,然后写指针向后移动。如果移动到数组的末尾,并且还有数据要写入的时候,回绕回到数组的开头,继续写入数据。
- 读指针(tail) :指向下一个将要被读取的数组的位置,和写指针类似,如果读指针度到了数组的末尾,还有数据要读取,它回绕回到数组的开头,继续读取数据。
3. 环形缓冲区读写区域
- 当写指针(head)在读指针(tail)前面的时候,表示数据是连续的。可以直接从读指针位置开始连续读取数据,如图
- 当读指针(tail) 在写指针前面的时候(head),表示数据跨越了缓冲区的末尾回到开头
4. 当数据读写超出区域时
4.1 数据写入超出可写区域时
- 等待: 如果没有足够的空间写入新数据,可以等待读取更多的数据,释放空间
- 覆盖旧数据: 如果最新的数据比旧数据更重要,可以选择覆盖旧数据。这通常在数据流应用中使用,比如实时视频流处理。
- 拒绝写入: 直接拒绝写入操作,并可能返回一个错误,告诉调用者缓冲区已满。
- 动态扩容: 如果实现允许,可以动态增加缓冲区的大小以容纳更多数据。这可能需要重新分配内存和移动现有数据,从而增加复杂性和性能开销
假设一个大小为10的环形缓冲区,当前写指针在位置7,读指针在位置2,可写区域是3、4、5、6(4个位置),但你想写入5个字节的数据。
如果选择覆盖,那么从位置3开始的5个字节会写入,这将导致位置2的数据被覆盖(在实际应用中需要小心处理读写指针)。
4.2 数据读取超出可读区域
- 部分读取: 只读取可读区域内的数据,并返回实际读取的长度。
- 等待: 如果缓冲区中的数据不足,可以等待直到有足够的数据被写入。
- 返回错误: 如果必须一次性读取足够的数据,不足时返回错误。
5. 代码
主要的就是读取环形缓冲区部分的代码
5.1 定义数据结构
typedef struct Ring_buffer{
unsigned char * buffer ;
int buffer_size ;
int write_p ;
int read_p ;
int data_len ;
}Ring_buffer, *Ring_buffer_p ;
5.2 对其初始化
// init the ring buffer
int init_ring_buffer(Ring_buffer_p ring_buffer , int max_size)
{
if(ring_buffer == NULL || max_size == 0)
{
return -1;
}
memset(ring_buffer , 0 , sizeof(*ring_buffer)) ;
ring_buffer->buffer =(unsigned char *) malloc(max_size*sizeof(unsigned char)) ;
if(ring_buffer->buffer == NULL)
{
printf("malloc the buffer fault\n") ;
return -1 ;
}
ring_buffer->buffer_size = max_size ;
ring_buffer->write_p = 0 ;
ring_buffer->read_p = 0 ;
ring_buffer->data_len = 0 ;
printf("init the ring buffer succesfull!\n") ;
return 0 ;
}
}
5.3 写环形缓冲区
参考上面的图
//write the ring buffer
int write_ring_buffer(Ring_buffer_p ring_buffer , unsigned char* in_buf , int in_data_len)
{
if(ring_buffer->data_len == ring_buffer->buffer_size)
{
printf("the ring buffer have been full\n") ;
return -1 ;
}
else if(in_data_len == 0)
{
printf("no data need to write to the ring buffer\n") ;
return -1;
}
// 1.计算剩余的空间
int free_space = 0 ;
if(ring_buffer->write_p >= ring_buffer->read_p)
{//如果写指针在读指针的前面, 则剩余的空间分为两部分
free_space =( ring_buffer->buffer_size - ring_buffer->write_p ) + (ring_buffer->read_p - 1);
}
else
{
free_space = ring_buffer->read_p -1 - ring_buffer->write_p;
}
//2.计算实际写入的数据长度
int need_write_len = in_data_len>free_space?free_space:in_data_len ;
printf("the auctual write data len is %d\n",need_write_len);
int first_write_len = 0 ;
int second_write_len = 0 ;
if(ring_buffer->write_p >= ring_buffer->read_p) //分为两部分可以写入
{
/*1.先判断第一部分剩余的空间是否可以写完需要写的数据*/
first_write_len = min(ring_buffer->buffer_size - ring_buffer->write_p,need_write_len) ;
memcpy(ring_buffer->buffer+ring_buffer->write_p, in_buf,first_write_len);
ring_buffer->write_p = (ring_buffer->write_p+first_write_len)%ring_buffer->buffer_size;
ring_buffer->data_len += first_write_len;
/*2.如果第一次写不完那么第二次进行写入*/
if(need_write_len>first_write_len)
{
int second_write_len = need_write_len - first_write_len ;
memcpy(ring_buffer->buffer,in_buf+first_write_len,second_write_len );
ring_buffer->write_p = (ring_buffer->write_p+first_write_len)%ring_buffer->buffer_size;
ring_buffer->data_len += first_write_len;
}
}
else
{
first_write_len = min(ring_buffer->read_p - ring_buffer->write_p -1 ,need_write_len);
memcpy(ring_buffer->buffer + ring_buffer->write_p,in_buf,first_write_len) ;
ring_buffer->write_p = (ring_buffer->write_p+first_write_len)%ring_buffer->buffer_size;
ring_buffer->data_len += first_write_len;
}
return need_write_len ;
}
5.4 读环形缓冲区
//read the ring buffer
int read_ring_buffer(Ring_buffer_p ring_buffer , unsigned char* out_buf , int out_data_len)
{
if(out_data_len == 0 || ring_buffer->data_len==0)
{
printf("read data length is 0\n");
return -1 ;
}
int read_data_len ;
read_data_len = out_data_len >ring_buffer->data_len ? ring_buffer->data_len:out_data_len;
printf("coule read data length is %d\n",read_data_len);
int first_read_len = 0 ;
int second_read_len =0 ;
if(ring_buffer->read_p >= ring_buffer->write_p)
{ //分为两部分进行读取
first_read_len = ring_buffer->buffer_size - ring_buffer->read_p ;
memcpy(out_buf,ring_buffer->buffer + ring_buffer->read_p,first_read_len);
ring_buffer->read_p = (ring_buffer->read_p+first_read_len)% ring_buffer->buffer_size ;
ring_buffer->data_len = ring_buffer->data_len - first_read_len ;
printf("第一部分读取成功\n");
if(read_data_len > first_read_len)
{
second_read_len = read_data_len - first_read_len ;
memcpy(out_buf+first_read_len, ring_buffer->buffer,second_read_len);
ring_buffer->read_p = second_read_len ;
ring_buffer->data_len = ring_buffer->data_len - second_read_len ;
printf("第二部分读取成功\n");
}
}
else
{//一次性全部读取
memcpy(out_buf,ring_buffer->buffer+ring_buffer->read_p,read_data_len);
ring_buffer->read_p = (ring_buffer->read_p+read_data_len)% ring_buffer->buffer_size ;
ring_buffer->data_len = ring_buffer->data_len - read_data_len ;
printf("一次性读取成功\n");
}
return read_data_len ;
}
5.5 测试
6 . 完整的代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#define min(a,b) ((a < b)?(a):(b))
#define MAX_RING_BUFFER_LEN 4
typedef struct Ring_buffer{
unsigned char * buffer ;
int buffer_size ;
int write_p ;
int read_p ;
int data_len ;
pthread_mutex_t lock ;
}Ring_buffer, *Ring_buffer_p ;
// init the ring buffer
int init_ring_buffer(Ring_buffer_p ring_buffer , int max_size)
{
if(ring_buffer == NULL || max_size == 0)
{
return -1;
}
memset(ring_buffer , 0 , sizeof(*ring_buffer)) ;
ring_buffer->buffer =(unsigned char *) malloc(max_size*sizeof(unsigned char)) ;
if(ring_buffer->buffer == NULL)
{
printf("malloc the buffer fault\n") ;
return -1 ;
}
ring_buffer->buffer_size = max_size ;
ring_buffer->write_p = 0 ;
ring_buffer->read_p = 0 ;
ring_buffer->data_len = 0 ;
pthread_mutex_init(ring_buffer->lock , NULL) ;
printf("init the ring buffer succesfull!\n") ;
return 0 ;
}
//write the ring buffer
int write_ring_buffer(Ring_buffer_p ring_buffer , unsigned char* in_buf , int in_data_len)
{
pthread_mutex_lock(ring_buffer->lock);
if(ring_buffer->data_len == ring_buffer->buffer_size)
{
printf("the ring buffer have been full\n") ;
pthread_mutex_unlock(ring_buffer->lock);
return -1 ;
}
else if(in_data_len == 0)
{
printf("no data need to write to the ring buffer\n") ;
pthread_mutex_unlock(ring_buffer->lock);
return -1;
}
// 1.计算剩余的空间
int free_space = 0 ;
if(ring_buffer->write_p >= ring_buffer->read_p)
{//如果写指针在读指针的前面, 则剩余的空间分为两部分
free_space =( ring_buffer->buffer_size - ring_buffer->write_p ) + (ring_buffer->read_p - 1);
}
else
{
free_space = ring_buffer->read_p -1 - ring_buffer->write_p;
}
//2.计算实际写入的数据长度
int need_write_len = in_data_len>free_space?free_space:in_data_len ;
printf("the auctual write data len is %d\n",need_write_len);
int first_write_len = 0 ;
int second_write_len = 0 ;
if(ring_buffer->write_p >= ring_buffer->read_p) //分为两部分可以写入
{
/*1.先判断第一部分剩余的空间是否可以写完需要写的数据*/
first_write_len = min(ring_buffer->buffer_size - ring_buffer->write_p,need_write_len) ;
memcpy(ring_buffer->buffer+ring_buffer->write_p, in_buf,first_write_len);
ring_buffer->write_p = (ring_buffer->write_p+first_write_len)%ring_buffer->buffer_size;
ring_buffer->data_len += first_write_len;
/*2.如果第一次写不完那么第二次进行写入*/
if(need_write_len>first_write_len)
{
int second_write_len = need_write_len - first_write_len ;
memcpy(ring_buffer->buffer,in_buf+first_write_len,second_write_len );
ring_buffer->write_p = (ring_buffer->write_p+first_write_len)%ring_buffer->buffer_size;
ring_buffer->data_len += first_write_len;
}
}
else
{
first_write_len = min(ring_buffer->read_p - ring_buffer->write_p -1 ,need_write_len);
memcpy(ring_buffer->buffer + ring_buffer->write_p,in_buf,first_write_len) ;
ring_buffer->write_p = (ring_buffer->write_p+first_write_len)%ring_buffer->buffer_size;
ring_buffer->data_len += first_write_len;
}
pthread_mutex_unlock(ring_buffer->lock);
}
//read the ring buffer
int read_ring_buffer(Ring_buffer_p ring_buffer , unsigned char* out_buf , int out_data_len)
{
pthread_mutex_lock(ring_buffer->lock);
if(out_data_len == 0 || ring_buffer->data_len==0)
{
printf("read data length is 0\n");
pthread_mutex_unlock(ring_buffer->lock);
return -1 ;
}
int read_data_len ;
read_data_len = out_data_len >ring_buffer->data_len ? ring_buffer->data_len:out_data_len;
printf("coule read data length is %d\n",read_data_len);
int first_read_len = 0 ;
int second_read_len =0 ;
if(ring_buffer->read_p >= ring_buffer->write_p)
{ //分为两部分进行读取
first_read_len = ring_buffer->buffer_size - ring_buffer->read_p ;
memcpy(out_buf,ring_buffer->buffer + ring_buffer->read_p,first_read_len);
ring_buffer->read_p = (ring_buffer->read_p+first_read_len)% ring_buffer->buffer_size ;
ring_buffer->data_len = ring_buffer->data_len - first_read_len ;
printf("第一部分读取成功\n");
if(read_data_len > first_read_len)
{
second_read_len = read_data_len - first_read_len ;
memcpy(out_buf+first_read_len, ring_buffer->buffer,second_read_len);
ring_buffer->read_p = second_read_len ;
ring_buffer->data_len = ring_buffer->data_len - second_read_len ;
printf("第二部分读取成功\n");
}
}
else
{//一次性全部读取
memcpy(out_buf,ring_buffer->buffer+ring_buffer->read_p,read_data_len);
ring_buffer->read_p = (ring_buffer->read_p+read_data_len)% ring_buffer->buffer_size ;
ring_buffer->data_len = ring_buffer->data_len - read_data_len ;
printf("一次性读取成功\n");
}
pthread_mutex_unlock(ring_buffer->lock);
return read_data_len ;
}
void printf_ring_buffer(Ring_buffer_p ring_buffer)
{
for(int i = ring_buffer->read_p ; i <= ring_buffer->data_len;++i)
{
printf("the ring_buffer[%d] data is : %d\n", i ,ring_buffer->buffer[i]);
}
}
int main(int argc , char *argv[])
{
int ret = 0 ;
Ring_buffer ring_buffer ;
init_ring_buffer(&ring_buffer , MAX_RING_BUFFER_LEN);
unsigned char in_buf[] = {2,5,3,4};
for(int i = 0 ; i < sizeof(in_buf)/sizeof(in_buf[0]) ; ++i)
{
printf("the in_buf[%d] data is: %d\n", i , in_buf[i]) ;
}
ret = write_ring_buffer(&ring_buffer ,&in_buf,sizeof(in_buf)) ;
printf_ring_buffer(&ring_buffer) ;
unsigned char out_buf[2] ;
ret = read_ring_buffer(&ring_buffer,&out_buf,2);
for(int i = 0 ; i < sizeof(out_buf)/sizeof(out_buf[0]);++i)
{
printf("the out_buf [%d] data is %d\n",i,out_buf[i]);
}
printf_ring_buffer(&ring_buffer) ;
return 0;
}