1. 原理
参考http://blog.chinaunix.net/uid-9407839-id-3954445.html
环形缓冲区通常有一个读指针和一个写指针。读指针指向环形缓冲区中可读的数据,写指针指向环形缓冲区中可写的缓冲区。通过移动读指针和写指针就可以实现缓冲区的数据读取和写入。在通常情况下,环形缓冲区的读用户仅仅会影响读指针,而写用户仅仅会影响写指针。如果仅仅有一个读用户和一个写用户,那么不需要添加互斥保护机制就可以保证数据的正确性。如果有多个读写用户访问环形缓冲区,那么必须添加互斥保护机制来确保多个用户互斥访问环形缓冲区。
图1、图2和图3是一个环形缓冲区的运行示意图。图1是环形缓冲区的初始状态,可以看到读指针和写指针都指向第一个缓冲区处;图2是向环形缓冲区中添加了一个数据后的情况,可以看到写指针已经移动到数据块2的位置,而读指针没有移动;图3是环形缓冲区进行了读取和添加后的状态,可以看到环形缓冲区中已经添加了两个数据,已经读取了一个数据。
2. 代码实现(以RT-Thread源代码为例)
结构体定义
struct rt_ringbuffer
{
rt_uint16_t read_index, write_index;
rt_uint8_t *buffer_ptr;
rt_uint16_t buffer_size;
};
初始化函数
void rt_ringbuffer_init(struct rt_ringbuffer *rb,
rt_uint8_t *pool,
rt_uint16_t size)
{
RT_ASSERT(rb != RT_NULL);
/* initialize read and write index */
rb->read_index = rb->write_index = 0;
/* set buffer pool and size */
rb->buffer_ptr = pool;
rb->buffer_size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
}
写入函数
rt_size_t rt_ringbuffer_put(struct rt_ringbuffer *rb,
const rt_uint8_t *ptr,
rt_uint16_t length)
{
rt_uint16_t size;
rt_uint16_t mask;
rt_uint16_t write_position;
RT_ASSERT(rb != RT_NULL);
mask = rb->buffer_size - 1;
/* whether has enough space */
size = rb->buffer_size - (rb->write_index - rb->read_index);
/* no space */
if (size == 0)
return 0;
/* drop some data */
if (size < length)
length = size;
write_position = (rb->write_index & mask);
if (rb->buffer_size - write_position> length)
{
/* read_index - write_index = empty space */
memcpy(&rb->buffer_ptr[write_position], ptr, length);
}
else
{
memcpy(&rb->buffer_ptr[write_position], ptr,
rb->buffer_size - write_position);
memcpy(&rb->buffer_ptr[0], &ptr[rb->buffer_size - write_position],
length - (rb->buffer_size - write_position));
}
rb->write_index += length;
return length;
}
其中
mask = rb->buffer_size - 1; read_position = rb->read_index & mask;
是可以实现写指针绕回到缓冲区头部后归0,注意该操作需要在获得size后才能执行。size为缓冲区空余的空间,若需要写入的数据长度超出size,则截断。若数据长度大于缓冲区尾部到当前写指针的长度,则需分两次写入操作。
读出函数
rt_size_t rt_ringbuffer_get(struct rt_ringbuffer *rb,
rt_uint8_t *ptr,
rt_uint16_t length)
{
rt_size_t size;
rt_uint16_t mask;
rt_uint16_t read_position;
RT_ASSERT(rb != RT_NULL);
/* whether has enough data */
mask = rb->buffer_size - 1;
size = rb->write_index - rb->read_index;
/* no data */
if (size == 0)
return 0;
/* less data */
if (size < length)
length = size;
read_position = rb->read_index & mask;
if (rb->buffer_size - read_position >= length)
{
/* copy all of data */
memcpy(ptr, &rb->buffer_ptr[read_position], length);
}
else
{
/* copy first and second */
memcpy(ptr, &rb->buffer_ptr[read_position],
rb->buffer_size - read_position);
memcpy(&ptr[rb->buffer_size - read_position], &rb->buffer_ptr[0],
length - rb->buffer_size + read_position);
}
rb->read_index += length;
return length;
}
其中size为当前缓冲区中含有数据的长度,若需要读出的数据长度超出size,则截断。若数据长度大于尾部到当前读指针的长度,则需分两次读取操作。
本文介绍了RingBuffer的工作原理,通过读写指针的移动描述了其数据读写过程,并提供了RT-Thread操作系统中RingBuffer的结构体定义、初始化及读写函数的示例。在多用户访问时,需要互斥保护机制以确保数据正确性。
1742

被折叠的 条评论
为什么被折叠?



