zoukankan      html  css  js  c++  java
  • 理解RTThead中使用的环形缓冲区 — Buffer Ring

    在RTT编程指南(V0.3.0)的P72页“信号量”一节提到了环形缓冲区(Buffer Ring)的使用。因为说是环形,导致一开始总是在脑海里构建一个圆环样的示意图,结果怎么也搞不明白,最后通过官方人士的解释和查了下资料终于搞明白了,这里记录一下。

    环形Buffer的特点:通常包含一个读指针(read_index)和一个写指针(write_index)。读指针指向环形 Buffer中第一个可读的数据,写指针指向环形Buffer中第一个可写的缓冲区。通过移动读指针和写指针就可以实现Buffer的数据读取和写入。在 通常情况下,环形Buffer的读用户仅仅会影响读指针,而写用户也仅仅会影响写指针。

    环形Buffer的原理:首先在内存里开辟一片区域(大小为 buffer_size),对于写用户,顺次往Buffer里写入东西,一直写到最后那个内存(buffer_size)时再将写指针指向内存区域的首地 址,即接下来的数据转个环放到最开始处,只有遇到Buffer里的有效存储空间为0时,才丢掉数据;对于读用户,顺次从Buffer里读出东西,一直写到 最后那个内存(buffer_size)时再将读指针指向内存区域的首地址,即接下来转个环从最开始处取数据。

    有效存储空间与buffer_size的区别:
    有效存储空间是指那些没有存放数据,或者以前存放过但已经处理过的数据,就是可用的空间大小;而buffer_size指的是总大小。

    通过上面介绍可知,环形Buffer仍然是一长条区域,只不过其空间会被循环使用而已。示意图如下,根据读写指针的位置可分为两种情况,其中阴影填充部分为数据/已用空间,空白的为可用空间,即有效存储空间。

    下面以编程指南的P72页上的部分示例代码为例:

    首先定义一个环形Buffer结构:

    struct rb
    {
    rt_uint16_t read_index, write_index; //读指针和写指针
    rt_uint8_t *buffer_ptr; //环形buffer指针
    rt_uint16_t buffer_size; //环形buffer大小
    };

    然后就是实现读/写操作了,对于写操作:

    /* 向环形buffer中写入数据 */
    static rt_bool_t rb_put(struct rb* rb, const rt uint8_t *ptr, rt_uint16_t length)
    {
    rt_size_t size;
     
    /* 判断是否有足够的剩余空间 */
    //图示的第一种情况,相减即为有效存储空间,就是中间那段空白区域了
    if (rb->read_index > rb->write_index)
    size = rb->read_index - rb->write_index;
    else // 图示的第二种情况,两段效存储空间之和
    size = rb->buffer_size - rb->write_index + rb->read_index;
     
    /* 没有多余的空间,即空间不足无法完成写入,直接返回错误 */
    if (size < length) return RT_FALSE;
     
    /* 以下均是指有效存储空间满足,可以完成写操作 */
    //图示的第一种情况,所以直接将数据从write_index处写入到buffer_ptr中即可
    if (rb->read_index > rb->write_index)
    {
    /* read_index - write_index 即为总的空余空间
    * 关于memcpy, 指的是将prt中长度为length的数据拷贝到第一个参数所指的地址
    * 其实每次写入都必须是从write_index(写指针)开始,
    * 因为write_index是指向环形Buffer中的第一个可写缓冲区
    */

    memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
     
    rb->write_index += length; //写操作完成后需要将写指针后移相应的长度
    }
    else //图示的第二种情况,有两段可用空间
    {
    //如果后半段空闲空间足够容纳,则直接拷贝即可
    if (rb->buffer_size - rb->write_index > length)
    {
    memcpy(&rb->buffer_ptr[rb->write_index], ptr, length);
    rb->write_index += length; //写指针后移相应的长度
    }
    else //如果后半段空间不足,则先将后半段装满,然后再将剩余的装到前半段之中
    {
    /*
    * write_index 后面剩余的空间不存在足够的长度,
    * 需要把部分数据复制到前面的剩余空间中
    */

    //先塞满后半段,注意大小不是length,而是后半段的长度
    memcpy(&rb->buffer_ptr[rb->write index], ptr, rb->buffer_size - rb->write_index);
     
    //此时只能从buffer_ptr的开始处进行写入了,长度为总长减去上次已经写入的
    memcpy(&rb->buffer_ptr[0], &ptr[rb->buffer_size - rb->write_index], length - (rb->buffer_size - rb->write_index));
     
    //写指针后移相应的长度,其实就是第二次写入的长度
    rb->write_index = length - (rb->buffer_size - rb->write_index);
    }
    }
    return RT_TRUE;
    }

    对于读操作:

    /* 从环形buffer中读出数据 */
    static rt_bool_t rb_get(struct rb* rb, rt_uint8 t *ptr, rt_uint16_t length)
    {
    rt_size_t size;
     
    /* 判断是否有足够的数据 */
    if (rb->read_index > rb->write_index) //图示的第一种情况,有效数据为前后两段的和
    size = rb->buffer_size - rb->read_index + rb->write_index;
    else // 图示的第二种情况, 中间一段为数据,直接相减即可
    size = rb->write_index - rb->read_index;
     
    /* 没有足够的数据,如果剩余数据不足,这直接返回错误 */
    if (size < length) return RT_FALSE;
     
    /* 以下均是指剩余数据量满足,可以完成读操作 */
    if (rb->read_index > rb->write_index) //图示的第一种情况,有两段数据
    {
    //如果后半段数据足够,则直接取用
    if (rb->buffer_size - rb->read_index > length)
    {
    //这里是将buffer_ptr里面取出长度为length的数据放到ptr之中;
    //切记总是从read_index(读指针)开始取
    memcpy(ptr, &rb->buffer_ptr[rb->read_index], length);
     
    rb->read_index += length; //读指针后移相应的长度
    }
    else //如果后半段不够用,则先取完后半段,然后从前半段取剩余的部分
    {
    /* read index的数据不够,需要分段复制 */
    //注意数据长度
    memcpy(ptr, &rb->buffer_ptr[rb->read index], rb->buffer_size - rb->read_index);
     
    //注意数据长度和存储的起始位置
    memcpy(&ptr[rb->buffer_size - rb->read_index], &rb->buffer_ptr[0], length - rb->buffer_size + rb->read_index);
    rb->read_index = length - rb->buffer_size + rb->read_index;
    }
    }
    else //图示的第二种情况,仅中间一段有数据,直接取用
    {
    memcpy(ptr, &rb->buffer_ptr[rb->read_index], length);
    rb->read_index += length; //读指针后移相应的长度
    }
    return RT_TRUE;
    }
  • 相关阅读:
    JavaScript-警告(alert 消息对话框)
    JavaScript中4种document.write()输出展示
    JavaScript-什么是函数
    JavaScript-判断语句(if...else)
    javascript里面什么是变量
    [学习笔记] IT项目管理
    [学习笔记] IT项目管理
    [学习笔记] Oracle基础增删改查用法
    [学习笔记] Oracle字段类型、建表语句、添加约束
    [学习笔记] RabbitMQ的简单使用
  • 原文地址:https://www.cnblogs.com/wzc0066/p/2949290.html
Copyright © 2011-2022 走看看