RingBuffer - 原理与实现

本文介绍了RingBuffer的工作原理,通过读写指针的移动描述了其数据读写过程,并提供了RT-Thread操作系统中RingBuffer的结构体定义、初始化及读写函数的示例。在多用户访问时,需要互斥保护机制以确保数据正确性。
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,则截断。若数据长度大于尾部到当前读指针的长度,则需分两次读取操作。

 

 

 

Linux内核中的无锁(lock-free)技术主要用于实现高效的并发数据结构,以提高系统的性能和吞吐量。其中,无锁环形缓冲区(lock-free ring buffer)是一种常用的数据结构,它可以高效地实现在多个线程之间传递数据的功能。 无锁环形缓冲区的实现原理如下: 1. 环形缓冲区的数据结构:无锁环形缓冲区由一个固定大小的环形数组和两个指针构成,一个是读指针,一个是写指针。读指针指向下一个将要读取的元素,写指针指向下一个将要写入的元素。 2. 原子操作:无锁环形缓冲区的实现依赖于原子操作(atomic operations),这些操作是在单个CPU指令中执行的,不会被其他线程中断。在Linux内核中,原子操作是通过宏定义实现的,如“atomic_add()”、“atomic_sub()”等。 3. 写入数据:当一个线程想要写入数据时,它首先需要检查缓冲区是否已满。如果缓冲区已满,则写入操作失败。如果缓冲区未满,则该线程会使用原子操作将数据写入缓冲区,并更新写指针。 4. 读取数据:当一个线程想要读取数据时,它首先需要检查缓冲区是否为空。如果缓冲区为空,则读取操作失败。如果缓冲区不为空,则该线程会使用原子操作将数据从缓冲区中读取,并更新读指针。 5. 线程同步:无锁环形缓冲区的实现不依赖于任何锁机制,因此可以避免锁竞争和死锁等问题。不过,在多个线程并发读写的情况下,需要使用一些同步机制来保证线程安全,如使用原子操作或者memory barrier等技术。 总的来说,无锁环形缓冲区是一种高效的并发数据结构,能够在多个线程之间高效地传递数据,提高系统的性能和吞吐量。在Linux内核中,无锁环形缓冲区的实现依赖于原子操作和线程同步技术,可以避免锁竞争和死锁等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值