精华内容
下载资源
问答
  • 高性能环形缓冲区设计
    2021-07-15 18:46:05

    来源:微信公众号「编程学习基地」

    绪论

    设计缘由

    工作后第一个项目是实现一个数据转发服务器,将视频数据通过websocket转发给手机app端。

    第一次在需要考虑网络因素,需要设计一个消息缓冲队列,用于存储大量数据。领导建议我利用环形缓冲区替换链式缓冲区减少memmove的次数,提高服务器性能。

    为什么要设计环形缓冲区

    目的:避免频繁的内存创建取消、分配,移动以提高服务器性能。内存一直只用了一块。

    环形缓冲区的实现原理

    环形缓冲区通常有一个读指针和一个写指针。读指针指向环形缓冲区中可读的数据,写指针指向环形缓冲区中可写的缓冲区。通过移动读指针和写指针就可以实现缓冲区的数据读取和写入。在通常情况下,环形缓冲区的读用户仅仅会影响读指针,而写用户仅仅会影响写指针。如果仅仅有一个读用户和一个写用户,那么不需要添加互斥保护机制就可以保证数据的正确性。如果有多个读写用户访问环形缓冲区,那么必须添加互斥保护机制来确保多个用户互斥访问环形缓冲区。

    图1、图2和图3是一个环形缓冲区的运行示意图。图1是环形缓冲区的初始状态,可以看到读指针和写指针都指向第一个缓冲区处;图2是向环形缓冲区中添加了一个数据后的情况,可以看到写指针已经移动到数据块2的位置,而读指针没有移动;图3是环形缓冲区进行了读取和添加后的状态,可以看到环形缓冲区中已经添加了两个数据,已经读取了一个数据。

    img

    判断“空”和“满”

    上述的操作并不复杂,不过有一个小小的麻烦:空环和满环的时候,R和W都指向同一个位置!这样就无法判断到底是“空”还是“满”。

    大体上有两种方法可以解决该问题。
    办法1:始终保持一个元素不用
      当空环的时候,R和W重叠。当W比R跑得快,追到距离R还有一个元素间隔的时候,就认为环已经满。当环内元素占用的存储空间较大的时候,这种办法显得很土(浪费空间)。
    办法2:维护额外变量
      如果不喜欢上述办法,还可以采用额外的变量来解决。比如可以用一个整数记录当前环中已经保存的元素个数(该整数>=0)。当R和W重叠的时候,通过该变量就可以知道是“空”还是“满”。

    环形缓冲区设计分两种模式

    模式一

    写入读取数据,不考虑读取数据的长度,读取数据的顺序为写入数据的顺序

    环形缓冲区测试代码

    #include "lwsBuffer.h"
    #include <stdio.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    int main()
    {
        char dataBuf[1024];
        void *temp = NULL;
        int readFd = open("lwsBuffer.cpp", O_RDONLY);
        int writeFd = open("temp.cpp", O_WRONLY | O_CREAT);
    
        int ret;
        memset(dataBuf, '\0', 1024);
        lwsBuffer Buff;
        while (1)
        {
            /* code */
            memset(dataBuf, '\0', sizeof(dataBuf));
            ret = read(readFd, dataBuf, sizeof(dataBuf));
            if (ret == 0)
            {
                printf("read end..");
                break;
            }
            else if (ret < 0)
                printf("read error ret:%d", ret);
            else
                printf("read  ret:%d\n", ret);
            ret = Buff.write(dataBuf, ret); //将 dataBuf 数据写入到Buff缓冲区
            printf("write ret:%d\n", ret);
        }
        printf("\n\ncurrent Buff total len:%d\n\n", Buff.getTotalLen());
        while (1)
        {
            char tempBuf[1024];
            memset(tempBuf,'\0',sizeof(tempBuf));
            ret = Buff.read(&temp); //从 Buff缓冲区中读取数据
            if (ret == -1)
                break;
            memmove(tempBuf, temp, ret);
            /* code */
            printf("total len:%d\n", Buff.getTotalLen());
            write(writeFd, tempBuf, ret);
        }
    
        close(readFd);
        close(writeFd);
        return 0;
    }
    

    makefile编译文件

    test:test.cpp
    	g++ -o test test.cpp lwsBuffer.cpp 
    .PHONY:clean
    clean:
    	rm -f test temp.cpp
    

    运行结果

    sh-4.3$ make
    g++ -o test test.cpp lwsBuffer.cpp 
    sh-4.3$ ls
    lwsBuffer.cpp  lwsBuffer.h  makefile  test  test.cpp
    sh-4.3$ ./test 
    read  ret:1024
    write ret:1024
    read  ret:1024
    write ret:1024
    read  ret:1024
    write ret:1024
    read  ret:964
    write ret:964
    read end..
    
    current Buff total len:4036
    
    total len:3012
    total len:1988
    total len:964
    total len:0
    sh-4.3$ ls
    lwsBuffer.cpp  lwsBuffer.h  makefile  temp.cpp  test  test.cpp
    

    执行后结果就是读取 lwsBuffer.cpp 里面的数据,储存到环形缓冲区,再从环形缓冲区读取数据写入到 temp.cpp

    应用场景和优缺点

    大量数据的转发

    优点:牺牲少量内存实现最少的数据拷贝memmove

    缺点:当转发的数据很大时,没一个对象浪费的内存在转发的数据大小之内

    ​ 当读取数据缓慢的时候会造成频繁的内存重分配,缓冲区变得越来越大。

    lwsBuffer.h

    #pragma once
    
    #ifndef _LWS_BUFFER_H_
    #define _LWS_BUFFER_H_
    #include <iostream>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <list>
    #define BUFFER_SIZE 1024
    class lwsBuffer
    {
    private:
        /* data */
        void* buffer;               /* 数据 */
        size_t bufMaxSize;          /* 最大的存储数据大小 */
        size_t rightWritePos;
        int readpos,writepos;       /* 读写位置 */
    
        size_t totalLen;            /* 存储的数据大小 */
        std::list<int> lenList;     /* 数据长度的链表 */
    public:
        lwsBuffer(/* args */);
        ~lwsBuffer();
    public:
        int write(void *data, int dataLen);
        int read(void** data);
        int getTotalLen();
    protected:
        int writeAfter(void* data, int dataLen);
        int writePre(void* data, int dataLen);
        void _remalloc();
    };
    #endif
    

    lwsBuffer.cpp

    #include "lwsBuffer.h"
    
    lwsBuffer::lwsBuffer(/* args */)
    {
        this->readpos = 0;
        this->writepos = 0;
        this->rightWritePos = 0;
        this->bufMaxSize = BUFFER_SIZE;
        this->buffer = malloc(this->bufMaxSize);
    
        this->totalLen = 0;
        this->lenList.clear();
    }
    
    lwsBuffer::~lwsBuffer()
    {
        if (buffer)
            free(buffer);
    }
    
    int lwsBuffer::write(void *data, int dataLen)
    {
        int ret = -1;
        if (dataLen <= 0 || data == NULL)
            return ret;
    
        /* writePos 在 readPos 前 / readPos == writePos push*/
        if (this->writepos >= this->readpos)
        {
            ret = writePre(data, dataLen);
        }
        /* readPos 在 writePos 前  push*/
        else if(this->writepos < this->readpos)
        {
            ret = writeAfter(data, dataLen);
        }
        return ret;
    }
    
    int lwsBuffer::writePre(void* data, int dataLen)
    {
        int ret = -1;
        /*  写入位置在前  */
        /*  buffer 后面有空间可以写入数据*/
        if ((this->writepos + dataLen) <= this->bufMaxSize) 
        {
            //将数据写入到 buffer 里面去
            memmove((char *)this->buffer + this->writepos, data, dataLen);
            /* 修改 writepos 偏移 */
            this->writepos = this->writepos + dataLen;
            /* 计算数据长度*/
            lenList.push_back(dataLen);
            totalLen += dataLen;
            ret = dataLen;
        }
        else
        {
            /*  buffer 后面没有空间可以写入数据  记录下罪*/
            this->rightWritePos = this->writepos;
            /* 修改 writepos 偏移 */
            this->writepos = 0;
            ret = writeAfter(data, dataLen);
        }
        return ret;
    }
    
    int lwsBuffer::writeAfter(void *data, int dataLen)
    {
        int ret = -1;
        /*  写入位置在后  */
        /*  writepos 到 readpos 有足够的空间可以写入数据  */
        if ((this->writepos + dataLen) < this->readpos)
        {
            //将数据写入到 buffer 里面去
            memmove((char *)this->buffer + this->writepos, data, dataLen);
            /* 修改 writepos 偏移 */
            this->writepos = this->writepos + dataLen;
            /* 计算数据长度*/
            lenList.push_back(dataLen);
            totalLen += dataLen;
            ret = dataLen;
        }
        else
        {
            this->_remalloc();
            ret = writePre(data, dataLen);
        }
        return ret;
    }
    
    void lwsBuffer::_remalloc()
    {
        /*  数据满了 */
        void *newBuf = malloc(this->bufMaxSize + BUFFER_SIZE / 2);
        /*      */
        memmove(newBuf, (char*)this->buffer + this->readpos, this->rightWritePos - this->readpos);
        memmove((char*)newBuf + this->rightWritePos - this->readpos, this->buffer, this->writepos);
        free(this->buffer);
        this->buffer = newBuf;
    
        this->writepos = this->rightWritePos - this->readpos + this->writepos;
        this->readpos = 0;
        this->bufMaxSize = this->bufMaxSize + BUFFER_SIZE / 2;
        // printf("buffMaxSize:%d, totalLen:%d ,list size:%d\n", (int)this->bufMaxSize, (int)this->totalLen, (int)lenList.size());
    }
    
    int lwsBuffer::read(void** data)
    {
        int ret = -1;
        if (this->totalLen > 0)
        {
            int dataLen = lenList.front();
            if((this->readpos + dataLen) <= this->bufMaxSize)
            {
                (*data) = (char *)this->buffer + this->readpos;
                // printf("\n读取位置: %d ,读取信息 len: %d, data:%s\n", this->readpos, dataLen, (char*)data);
                /* 修改 writepos 偏移 */
                this->readpos += dataLen;
                /* 计算数据长度*/
                this->totalLen -= dataLen;
                lenList.pop_front();
                ret = dataLen;
            }
            else{
                printf("从头开始读取\n");
                this->readpos = 0;
                (*data) = (char *)this->buffer + this->readpos;
    
                // printf("\n读取位置: %d ,读取信息 len:%d, data:%s\n", this->readpos, dataLen, (char*)data);
                this->readpos += dataLen;
                /* 计算数据长度*/
                this->totalLen -= dataLen;
                lenList.pop_front();
                ret = dataLen;
            }
        }
        return ret;
    }
    
    int lwsBuffer::getTotalLen()
    {
        return this->totalLen;
    }
    

    模式二

    写入读取数据,写入指定长度数据,和读取指定长度数据,模仿QBuffer的读写功能

    环形缓冲区测试代码

    #include "lwsBuffer.h"
    #include<stdio.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    
    int main()
    {
        char buf[1024],temp[1024];
        int readFd = open("lwsBuffer.cpp",O_RDONLY);
        int writeFd = open("temp.cpp",O_WRONLY|O_CREAT,0666);
        
        int ret;
        memset(buf, '\0', 1024);
        memset(temp, '\0', 1024);
        lwsBuffer Buff;
        while (1)
        {
            /* code */
            ret = read(readFd, buf, sizeof(buf));
            if (ret == 0)
                break;
            int dataLen = Buff.write(buf,ret);  //将数据保存到环形缓冲区
            memset(buf, '\0', 1024);
            printf("dataLen:%d\n",dataLen);
        }
    
        while(1)
        {
            memset(temp,'\0',sizeof(temp));
            ret = Buff.read(temp, 1024);    //从环形缓冲区中读取数据
            if (ret == -1)
                break;
            /* code */
            ret = write(writeFd, temp, ret);
            printf("write ret:%d\n", ret);
        }
    
        close(readFd);
        close(writeFd);
        return 0;
    }
    

    makefile编译文件

    test:test.cpp
    	g++ -o test test.cpp lwsBuffer.cpp 
    .PHONY:clean
    clean:
    	rm -f test temp.cpp
    

    运行结果

    sh-4.3$ make
    g++ -o test test.cpp lwsBuffer.cpp 
    sh-4.3$ ./test 
    dataLen:1024
    dataLen:1024
    dataLen:1024
    dataLen:766
    write ret:1024
    write ret:1024
    write ret:1024
    write ret:766
    

    应用场景和优缺点

    适用于少量数据的频繁读写

    优点:多次memmove实现占空间最少的快速读写

    缺点:当转发的数据很大且很频繁时,多次memmove会导致性能的减少

    lwsBuffer.h

    #pragma once
    
    #ifndef _LWS_BUFFER_H_
    #define _LWS_BUFFER_H_
    #include <iostream>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #define BUFFER_SIZE 1024
    class lwsBuffer
    {
    private:
        /* data */
        void* buffer;               /*数据*/
        size_t bufMaxSize;          /* 最大的存储数据大小 */
        size_t bufLen;              /* 存储的数据大小 */
        int readpos,writepos;       /* 读写位置 */
    public:
        lwsBuffer(/* args */);
        ~lwsBuffer();
    public:
        int write(void *data, int dataLen);
        int read(void *data, int dataLen);
    
    protected:
        int writeAfter(void* data, int dataLen);
        int writePre(void* data, int dataLen);
        void _remalloc();
    };
    
    #endif
    

    lwsBuffer.cpp

    #include "lwsBuffer.h"
    
    lwsBuffer::lwsBuffer(/* args */)
    {
        this->readpos = 0;
        this->writepos = 0;
        this->bufLen = 0;
        this->bufMaxSize = BUFFER_SIZE;
        this->buffer = malloc(this->bufMaxSize);
    }
    
    lwsBuffer::~lwsBuffer()
    {
        if (buffer)
            free(buffer);
    }
    
    int lwsBuffer::write(void *data, int dataLen)
    {
        int ret = -1;
        /* writePos 在 readPos 前 / readPos == writePos push*/
        if (this->writepos >= this->readpos)
        {
            ret = writePre(data, dataLen);
        }
        /* readPos 在 writePos 前  push*/
        else if(this->writepos < this->readpos)
        {
            ret = writeAfter(data, dataLen);
        }
        return ret;
    }
    
    int lwsBuffer::writePre(void* data, int dataLen)
    {
        int ret = -1;
        /*  写入位置在前  */
        /*  buffer 后面有空间可以写入数据  预留一个字节不存储数据,用于区分*/
        if ((this->writepos + dataLen) <= this->bufMaxSize)
        {
            //将数据写入到 buffer 里面去
            memmove((char *)this->buffer + this->writepos, data, dataLen);
            // printf("即将 push %d 消息 on:%d :%s\n", dataLen, prod->writepos, (char *)prod->buffer + prod->writepos + LWS_PRE);
            this->writepos = this->writepos + dataLen;
            ret = dataLen;
            bufLen += dataLen;
        }
        else
        {
            /*  buffer 后面没有空间可以写入数据  记录下*/
            int temp = this->bufMaxSize - this->writepos;
            temp = writePre(data, temp);
            this->writepos = 0;
            ret = writeAfter((char*)data + temp, dataLen - temp) + temp;
        }
        return ret;
    }
    
    int lwsBuffer::writeAfter(void *data, int dataLen)
    {
        int ret = -1;
        /*  写入位置在后  */
        /*  writepos 到 readpos 有足够的空间可以写入数据  */
        if ((this->writepos + dataLen) < this->readpos)
        {
            //将数据写入到 buffer 里面去
            memmove((char *)this->buffer + this->writepos, data, dataLen);
            this->writepos = this->writepos + dataLen;
            ret = dataLen;
            bufLen += dataLen;
        }
        else
        {
            this->_remalloc();
            ret = writePre(data, dataLen);
        }
        return ret;
    }
    
    void lwsBuffer::_remalloc()
    {
        /*  数据满了 */
        void *newBuf = malloc(this->bufMaxSize + BUFFER_SIZE / 2);
        /*      */
        memmove(newBuf, (char*)this->buffer + this->readpos, this->bufMaxSize - this->readpos);
        memmove((char*)newBuf + this->bufMaxSize - this->readpos, this->buffer, this->writepos);
        free(this->buffer);
        this->buffer = newBuf;
    
        this->writepos = this->bufMaxSize - this->readpos + this->writepos;
        this->readpos = 0;
        this->bufMaxSize = this->bufMaxSize + BUFFER_SIZE / 2;
        // printf("buffMaxSize:%d, bufLen:%d\n", (int)this->bufMaxSize,(int)this->bufLen);
    }
    
    int lwsBuffer::read(void *data, int dataLen)
    {
        int ret = -1;
        if (this->bufLen > 0)
        {
            if (dataLen > this->bufLen)
            {
                dataLen = this->bufLen;
            }
            if ((this->readpos + dataLen) <= this->bufMaxSize)
            {
                memmove(data, (char *)this->buffer + this->readpos, dataLen);
                // printf("\n读取位置: %d ,读取信息 len:%d,data:%s\n", this->readpos, dataLen, (char *)data);
                this->readpos += dataLen;
                this->bufLen -= dataLen;
                ret = dataLen;
            }
            else
            {
                int temp = this->bufMaxSize - this->readpos;
                temp = read(data, temp);
                this->readpos = 0;
                
                memmove((char*)data + temp, (char *)this->buffer, dataLen - temp);
                // printf("\n读取位置: %d ,读取信息 len:%d, data:%s\n", this->readpos, dataLen, (char *)data);
                this->readpos = this->readpos + dataLen - temp;
                this->bufLen = this->bufLen - (dataLen - temp);
                ret = dataLen;
            }
        }
        return ret;
    }
    
    更多相关内容
  • 本文主要是介绍 C语言实现环形缓冲区,并附有详细实现代码,具有一定的参考价值,希望能帮助有需要的小伙伴
  • 打包环提供了环形缓冲区的简单实现。 用法 var DefaultCapacity int = 10 未初始化的环形缓冲区的DefaultCapacity。 更改此值只会影响更改后创建的环形缓冲区。 环型 type Ring struct { sync. Mutex } 类型...
  • 基于环形缓冲区的CAN驱动模块、电子技术,开发板制作交流
  • 环形缓冲

    2017-11-07 20:49:56
    环形缓冲区的优点就不赘述了,此VS2015工程封装好环形缓冲区的接口,Boost库的二次封装,有测试示例。如果有编译问题,选择x86编译,记得要配置boost库环境。PS:如果可以的话,大量使用boost库,用起来很方便。
  • 该工程包含了整个实现代码,并添加了注释,提供了软件计时器多任务创建及调度接口函数,以及环形缓冲区完整接口函数。
  • 主要为大家详细介绍了C#环形缓冲区(队列)完全实现代码,感兴趣的小伙伴们可以参考一下
  • 假设在环形缓冲区的中心写入1(确切的起始位置在环形缓冲区中并不重要): 然后,假设将另外两个字节(23)添加到环形缓冲区,它们将放在1之后: 如果删除了两个字节,则环形缓冲区内部的两个最早加入的值将被删除。 ...
  • 环形缓冲区 Go中的循环缓冲区(环形缓冲区),已实现io.ReaderWriter接口 rb := New ( 1024 ) // write rb . Write ([] byte ( "abcd" )) fmt . Println ( rb . Length ()) fmt . Println ( rb . Free ()) //...
  • C语言环形缓冲

    2017-03-10 20:29:31
    C语言实现环形缓冲区,可供多线程读写操作
  • 分析了VxWorks嵌入式操作系统的驱动程序功能设计,以多串口通信驱动为例,结合看门狗定时器和中断处理机制,采用VxWorks系统自身提供的环形缓冲区管理接口库函数,设计了一种更为可靠高效、性能稳定的字符设备底层...
  • 这种处理方式是没有缓冲区的,当数量太大的时候,亦或者当数据接收太快的时候,我们来不及处理已经收到的数据, 那么,当再次收到数据的时候,就会将之前还未处理的数据覆盖掉。那么就会出现丢包的现象了,对我们的...
  • STM32进阶之串口环形缓冲区实现 FIFO,代码精简,易实现。
  • 缓冲 Ringbuf 是 go 语言的 ringbuffer 的简单无锁​​实现。
  • 内容尽管有名称,但该存储库包含两个不同的环形缓冲区的实现:线性环形缓冲区:include / bev / linear_ringbuffer.hpp IO缓冲区:include / bev / io_buffer.hpp此顶级自述文件主要由目录提供。尽管有名称,该存储...
  • RingBuffer_环形缓冲区_

    2021-10-02 08:50:37
    能够实现队列和堆栈操作,支持任意数据类型。
  • 环形缓冲测试.rar

    2019-07-24 09:14:34
    STM32F103ZET6芯片,配置串口,创建环形缓冲,通过串口收发,可以做到无丢包,STM32F103ZET6芯片,配置串口,创建环形缓冲,通过串口收发,可以做到无丢包,STM32F103ZET6芯片,配置串口,创建环形缓冲,通过串口...
  • Go中的线程安全循环缓冲区(环形缓冲区),实现了io.ReaderWriter接口
  • ringbuf无锁单生产者单消费者(SPSC)FIFO环形缓冲区,可直接访问内部数据。 概述RingBuffer是最初的结构ringbuf无锁单生产者单消费者(SPSC)FIFO环形缓冲区,可直接访问内部数据。 概述RingBuffer是代表环形缓冲区...
  • 环形缓冲区实现类(RingBuffer)
  • 概述Vxwroks 环形缓冲模块主要定义在rngLib.c和rngLib.h中,对于数据结构比较了解的小伙伴应该知道,环形缓冲实际就是一个双向循环队列。注意关于循环队列,小编之前在《也没想...

    概述

    Vxwroks 环形缓冲模块主要定义在rngLib.c和rngLib.h中,对于数据结构比较了解的小伙伴应该知道,环形缓冲实际就是一个双向循环队列。

    注意

    关于循环队列,小编之前在《也没想象中那么神秘的数据结构-先来后到的“队列”(循环队列)》一文中有过详细阐述,这里就不在过多的赘述了。

    开发环境:vxWorks6.9.4,workbench3.3.5,开发板:TLZ7x-EasyEVM-A3。

    另外,小编所有文章均是自己亲手编写验证,若需要小编的工程代码,请关注公众号,后台回复需要的工程文件如想要本文中的工程源文件可回复“实时系统vxWorks - 环形缓冲工程文件”获取。

    以下为工程目录文件内容。有需要的小伙伴后台发送相关信息给小编获取。

    文件内容如下:

    obj:存放目标文件,包含vxWorks镜像,应用程序目标文件。

    rng_test:vxWorks应用工程。

    接口

    官方接口

    官方环形缓冲接口定义主要包含在rngLib.h头文件中。

    环形缓冲接口定义

    typedef struct        /* RING - ring buffer */
    {
        size_t pToBuf;        /* 写指针 */
        size_t pFromBuf;    /* 读指针 */
        size_t bufSize;      /* 数据长度 */
        char *buf;              /* 存放数据 */
    } RING;
    
    /* END_HIDDEN */
    typedef RING *RING_ID;
    

    创建/删除环形缓冲

    /**
     * @创建环形缓冲
     * @nbytes: 缓冲尺寸
     * @成功返回环形缓冲ID,失败返回NULL。
     */
    extern RING_ID rngCreate (size_t nbytes);
    
    /**
     * @删除环形缓冲
     * @ringId: 环形缓冲ID   
     */
    extern void rngDelete (RING_ID ringId);
    

    判断缓冲是否空/满

    /**
     * @判断环形缓冲是否为空
     * @ringId: 环形缓冲ID   
     * @不为空返回0,否则返回其它。
     */
    extern BOOL rngIsEmpty (RING_ID ringId);
    
    /**
     * @判断环形缓冲是否为满
     * @ringId: 环形缓冲ID   
     * @不为满返回0,否则返回其它。
     */
    extern BOOL rngIsFull (RING_ID ringId);
    

    读写数据

    /**
     * @从环形缓冲中读取数据
     * @ringId: 环形缓冲ID         buffer:数据         maxbytes:长度
     * @返回实际读取到的数据长度。
     */
    extern size_t rngBufGet (RING_ID rngId, char *buffer, size_t maxbytes);
    
    /**
     * @写数据到环形缓冲
     * @ringId: 环形缓冲ID         buffer:数据         nbytes:长度  
     * @返回实际写入数据长度
     */
    extern size_t rngBufPut (RING_ID rngId, char *buffer, size_t nbytes);
    

    获取缓冲区剩余空间/已占空间

    /**
     * @获取环形缓冲剩余空间长度
     * @ringId: 环形缓冲ID 
     * @返回剩余空间长度。
     */
    extern size_t     rngFreeBytes (RING_ID ringId);
    
    /**
     * @获取环形缓冲已占空间长度
     * @ringId: 环形缓冲ID
     * @返回已占空间长度。
     */
    extern size_t     rngNBytes (RING_ID ringId);
    

    清空缓冲

    /**
     * @清空环形缓冲
     * @ringId: 环形缓冲ID
     */
    extern void rngFlush (RING_ID ringId);
    

    环形缓冲对象接口

    属性定义

    /* 环形缓冲类 */
    struct t_rng {
        u8_t            flag;   /* 创建标志,=1已创建,=0未创建 */
        RING_ID       id;      /* 环形缓冲 */ 
        struct t_sem    msem;   /* 环形缓冲互斥信号量 */ 
    };
    

    保存数据到环形缓冲

    /**
     * @保存数据到环形缓冲
     * @p_rng:环形缓冲类   buf: 数据   len:长度
     **/
    void put_rng(struct t_rng *p_rng, s8_t *buf, s32_t len);
    

    获取环形缓冲数据

    /**
     * @获取环形缓冲数据
     * @p_rng:环形缓冲类    buf装载数据, len数据长度
     * 成功返回实际数据长度,失败返回ERROR
     **/
    s32_t get_rng(struct t_rng *p_rng, s8_t *buf, s32_t len);
    

    创建环形缓冲

    /**
     * @创建环形缓冲
     * @p_rng:环形缓冲类   nbyte申请缓冲大小, name互斥信号名
     **/
    void create_rng(struct t_rng *p_rng, size_t nbytes, s8_t *name);
    

    示例

    ★示例创建两个任务(生产者/消费者),生产者向缓冲写入数据,消费者从缓冲读取数据,缓冲访问时使用互斥信号量进行互斥。

    ★关于信号量和任务部分可参加小编的《实时系统vxWorks - 信号量(重要)》和《实时系统vxWorks - 任务(重要)》文章。

    ★包含环形缓冲类rng.c/rng.h和演示程序main.c(已验证通过)。

     rng.h

    /**
     * @Filename : rng.h
     * @Revision : $Revision: 1.00 $
     * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅)
     * @Description : 环形缓冲类,在系统自带环形缓冲基础上封装互斥信号量
    **/
    
    #ifndef __RNG_CLASS_H__
    #define __RNG_CLASS_H__
    
    #include <vxWorks.h>  
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <rngLib.h>
    #include "sem.h"
    
    extern struct sem sem; /* 保存系统信号量信息 */
    
    /* 环形缓冲类 */
    struct t_rng {
        u8_t            flag;   /* 创建标志,=1已创建,=0未创建 */
        RING_ID       id;      /* 环形缓冲 */ 
        struct t_sem    msem;   /* 环形缓冲互斥信号量 */ 
    };
    
    /**
     * @保存数据到环形缓冲
     * @p_rng:环形缓冲类   buf: 数据   len:长度
     **/
    void put_rng(struct t_rng *p_rng, s8_t *buf, s32_t len);
    
    /**
     * @获取环形缓冲数据
     * @p_rng:环形缓冲类    buf装载数据, len数据长度
     * 成功返回实际数据长度,失败返回ERROR
     **/
    s32_t get_rng(struct t_rng *p_rng, s8_t *buf, s32_t len);
    
    /**
     * @创建环形缓冲
     * @p_rng:环形缓冲类   nbyte申请缓冲大小, name互斥信号名
     **/
    void create_rng(struct t_rng *p_rng, size_t nbytes, s8_t *name);
    
    #endif
    

     rng.c

    /**
     * @Filename : rng.c
     * @Revision : $Revision: 1.00 $
     * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅)
     * @Description : 环形缓冲类,在系统自带环形缓冲基础上封装互斥信号量
    **/
    
    #include "rng.h"
    
    /**
     * @保存数据到环形缓冲
     * @p_rng:环形缓冲类   buf: 数据   len:长度
     **/
    void put_rng(struct t_rng *p_rng, s8_t *buf, s32_t len)
    {  
        if (p_rng->flag == 0)
            return;
    
        get_sem(&p_rng->msem);                  
        if (!(rngIsFull(p_rng->id)))
            rngBufPut(p_rng->id, buf, len); 
        lose_sem(&p_rng->msem); 
    }
    
    /**
     * @获取环形缓冲数据
     * buf装载数据, len数据长度
     * 成功返回实际数据长度,失败返回ERROR
     **/
    s32_t get_rng(struct t_rng *p_rng, s8_t *buf, s32_t len)
    {  
        s32_t rLen = ERROR;
    
        if (p_rng->flag == 0)
            return ERROR;
    
        get_sem(&p_rng->msem);                  
        if (!(rngIsEmpty(p_rng->id)))
            rLen = rngBufGet(p_rng->id, buf, len);
        lose_sem(&p_rng->msem); 
    
        return (rLen);
    }
    
    /**
     * @创建环形缓冲
     * nbyte申请缓冲大小, name互斥信号名
     **/
    void create_rng(struct t_rng *p_rng, size_t nbytes, s8_t *name)
    {
        if (p_rng->flag == 1)
            return;
    
        p_rng->id = rngCreate(nbytes);
    
        p_rng->msem.sem = NULL;
        p_rng->msem.type = SEM_MUTEX;   
        strcpy(p_rng->msem.name, name);
        resgister_sem(&sem, &p_rng->msem);  
    
        p_rng->flag = 1;
    }
    

     main.c

    /**
     * @Filename : main.c
     * @Revision : $Revision: 1.00 $
     * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅)
     * @Description : 环形缓冲类使用示例
    **/
    
    #include <vxWorks.h>     
    #include <math.h>    
    #include "stdioLib.h"     
    #include "strLib.h"     
    #include "task.h"
    #include "sem.h"
    #include "rng.h"
    #include "feng_type.h"
    
    #define RNG_SIZE    100 /* 缓冲大小 */
    struct sem     sem; /* 保存系统信号量信息 */
    struct t_task s_task, s_task1;
    struct t_sem s_sem, s_sem1;
    struct t_rng rng;
    
    /**
     * @生产者:定时存储数据到环形缓冲
    **/
    static void _thread(void)
    {  
        static int cnt = 77;
    
        while (1) { 
            get_sem(&s_sem);        
            put_rng(&rng, &cnt, sizeof(int));       
            printf("put data : %d...\n", cnt++);
        }
    }
    
    /**
     * @消费者:定时从环形缓冲中取出数据
    **/
    static void _thread1(void)
    {  
        int cnt1 = 0;
    
        while (1) { 
            get_sem(&s_sem1);
    
            if (get_rng(&rng, &cnt1, sizeof(int)) > 0)
                printf("get data : %d...\n", cnt1);
        }
    }
    
    /**
     * @创建任务
     * @p_task:任务类    name:任务名        thread:函数
    **/
    void _create_task(struct t_task *p_task, char *name, FUNCPTR thread)
    {
        strcpy(p_task->name, name);     
        p_task->options    = VX_FP_TASK;
        p_task->stackSize  = 50 * 1024;
        p_task->pFunc      = thread;
        p_task->tid        = NULL;
        p_task->core       = 0;
        p_task->priority   = 102;
    
        create_task(p_task);
        start_task(p_task);
    }
    
    /**
     * @创建信号量
     * @p_sem:信号量类        name:信号量名       type:类型
    **/
    void _create_sem(struct t_sem *p_sem, char *name, E_SEM_TYPE type)
    {
        strcpy(p_sem->name, name);      
        p_sem->period = 100;
        p_sem->type = type;
        p_sem->sem = SEM_ID_NULL;
    
        resgister_sem(&sem, p_sem);
    }
    
    
    int main(void)
    {
        sysClkRateSet(100);         /* 时间片设置 */
    
        create_rng(&rng, RNG_SIZE, "my_rng");
    
        _create_sem(&s_sem, "my_sem", SEM_BIN);
        _create_sem(&s_sem1, "my_sem1", SEM_BIN);
    
        _create_task(&s_task, "my_task", (FUNCPTR)_thread);
        _create_task(&s_task1, "my_task1", (FUNCPTR)_thread1);
    
        while (1) { 
            taskDelay(500);     /* 5s */
            lose_sem_by_name(&sem, "my_sem");
            lose_sem_by_name(&sem, "my_sem1");  
        }
    
        return 0;
    }
    

    验证

    使用环形缓冲之前需要先添加INCLUDE_RNG_BUF组件。

    打开镜像工程,选择kernel Configuration。按住Ctrl+F,输入rng,找到ring buffers,添加组件。

    添加完组件后,编译镜像,将镜像拷贝到目标机加载指定目录。

    创建应用工程rng_test,输入相关测试代码,运行后如下图所示。

    注意:若不知道工程如何创建以及运行,可参见小编文章《实时系统vxWorks - 任务(重要)》和《实时系统vxWorks - 加载应用程序的方法》。

    往期 · 推荐

    实时系统vxWorks - 任务(重要)

    实时系统vxWorks - 加载应用程序的方法

    实时系统vxWorks - 信号量(重要)

    实时系统vxWorks - 虚拟机环境搭建

    也没想象中那么神秘的数据结构-先来后到的“队列”(循环队列)

    关注

    更多精彩内容,请关注微信公众号:不只会拍照的程序猿,本人致力分享linux、设计模式、C语言、嵌入式、编程相关知识,也会抽空分享些摄影相关内容,同样也分享大量摄影、编程相关视频和源码,另外你若想要获得更多内容教程请关注公众号:不只会拍照的程序猿

    展开全文
  • 环形缓冲区实现原理

    2013-08-27 09:15:25
    在通信程序中,经常使用环形缓冲区作为数据结构来存放通信中发送和接收的数据。环形缓冲区是一个先进先出的循环缓冲区,可以向通信程序提供对缓冲区的互斥访问。
  • C语言构建环形缓冲

    2021-08-28 23:55:00
    1、环形缓冲区原理 环列队列逻辑上将数组元素array[0]与array[LEN-1]连接起来,形成一个存放队列的环形空间。实际操作中为了方便读写,采用head和tail分别指向可以读的位置和可以写的位置。 环形

    它逻辑上是一个首尾相连的FIFO结构,具体实现上采用简单的线性数组。通过额外的辅助标志(head、tail)能很快知道队列的使用情况(是满还是为空)。正因为其简单高效的原因,甚至在硬件都实现了环形队列。

    环形队列广泛用于网络数据收发、程序间的大量数据交换(比如内核与应用程)、硬件接收大量数据

    1、环形缓冲区原理

    image-20210828164243830

    • 环列队列逻辑上将数组元素array[0]与array[LEN-1]连接起来,形成一个存放队列的环形空间。实际操作中为了方便读写,采用headtail分别指向可以读的位置和可以写的位置。
    • 环形队列的关键是判断队列为空,还是为满。一般有两种方法:
      • 一是附加一个标志位tag
      • 当head赶上tail,队列空,则令tag=0
      • 当tail赶上head,队列满,则令tag=1
      • 二是在队尾结点与队首结点之间留有1个元素的空间
        • 队列空: head==tail
        • 队列满: (tail+1)% MAXN ==head

    2、预留1个空位的环形队列

    • 开始时head和tail指向同一个地址,但随着数据的push和poll,head和tail之间永远预留一个单元空间。如下图所示即为一个满队列。但箭头所指的位置不准确。tail应该指向空位,head指向9。即从头出去,从尾巴进来。

    image-20210828164743425

    • 数据结构
    struct ring_queue{  
        unsigned int head;   
        unsigned int tail;  
        unsigned int size; 	//环形缓冲区容量
        int *array;   		//实际缓冲区(数组)的首地址
    }; 
    
    • 规则
      • head指向可读位置(地址存有数据),tail指向可写位置(地址无数据)。
      • 初始化状态: head = tail = 0;
      • 判定队列为空:head == tail
      • 队列为满:**( (tail+1) % SIZE ) == head **
      • 入队操作:若队列不满,则写入。
      • 出队操作:若队列不空,则读出。
      • 缓冲区必须是连续的内存空间,可以通过静态数组变量或局部数组变量的方式定义,但绝不能从堆上分配(malloc),因为malloc分配的内存空间是不连续的!!!
    • 头文件
    #ifndef __RINGQ_H__
    #define __RINGQ_H__
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
        
    typedef struct {  
        unsigned int head;   
        unsigned int tail;
        unsigned int size;
        int *array;   
    }RINGQ;    
    
    #define ringq_is_empty(q) (q->head == q->tail)
    #define ringq_is_full(q) (((q->tail+1)%q->size) == q->head )
    
    int ringq_init(RINGQ * ringqp, int * array_ptr, unsigned size);
    int ringq_free(RINGQ * ringqp);
    int ringq_push(RINGQ * ringqp,int data);
    int ringq_poll(RINGQ * ringqp,int * val);
    void ringq_display(RINGQ * ringqp);
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif
    
    • c文件
      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include "ringq.h"
      4 
      5 int ringq_init(RINGQ * ringqp, int * array_ptr, unsigned size)
      6 {
      7     /*
      8        不能从堆里分配空间!!!
      9        因为堆里的空间不是连续的,而是通过链表链接的一个空间串
     10     if(!(ringqp->array = (int *)malloc(size))){
     11         printf("Malloc failed!\n");
     12        return -1;
     13     }
     14     */
     15     ringqp->array = array_ptr;
     16     ringqp->size = size;
     17     ringqp->head = 0;
     18     ringqp->tail = 0;
     19     return 0;
     20 }
     21 
     22 int ringq_free(RINGQ * ringqp)
     23 {
     24     free(ringqp->array);
     25     return 0;
     26 }
     27 
     28 
     29 int ringq_push(RINGQ * ringqp,int data)
     30 {
     31     if(ringq_is_full(ringqp))
     32     {
     33         printf("ringq is full.\n");
     34         return -2;
     35     }
     36     ringqp->array[ringqp->tail] = data;
     37     ringqp->tail = (ringqp->tail + 1) % ringqp->size ;
     38     return 0;
     39 }
     40 
     41 
     42 int ringq_poll(RINGQ * ringqp,int * val)
     43 {
     44    if(ringq_is_empty(ringqp))
     45     {
     46         printf("ringq is empty.\n");
     47         return -3;
     48     }
     49     *val = ringqp->array[ringqp->head];
     50     ringqp->head = (ringqp->head + 1) % ringqp->size ;
     51     return 0;
     52 }
     53 
     54 void ringq_display(RINGQ * ringqp)
     55 {
     56     int  i =0;
     57     unsigned head = ringqp->head;
     58     unsigned tail = ringqp->tail;
     59     unsigned size = ringqp->size;
     60 
     61     if(ringq_is_empty(ringqp))
     62     {
     63         printf("ringq is empty.\n");
     64         return;
     65     }
     66     while(head != tail){
     67         printf("%04d ",ringqp->array[head]);
     68         i++;
     69         if(i == 5){
     70             printf("\n");
     71             i = 0;
     72         }
     73         head = (head + 1)%(size);
     74     }
     75     printf("\n");
     76     return;
     77 }
    
    • 测试文件
      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include "ringq.h"
      4 
      5 #define RQ_SIZE 50 
      6 
      7 int main(void)
      8 {
      9     int data_in = 0;
     10     int data_out = 0;
     11     int select = 0;
     12 
     13     int array[RQ_SIZE]={};
     14     RINGQ rq, *rqp;
     15     rqp = &rq;
     16 
     17     ringq_init(rqp, array, RQ_SIZE);
     18     ringq_display(rqp);
     19 
     20     int index = RQ_SIZE - 1;    //allways a bank between head and tail
     21     while (index > 0){
     22         ringq_push(rqp,1);
     23         index -= 1;
     24     }
     25 
     26     ringq_display(rqp);
     27 
     28     while (index < RQ_SIZE-1){
     29         ringq_poll(rqp,&data_out);
     30         index += 1;
     31     }
     32 
     33     ringq_display(rqp);
     34 
     35     while (index > 0){
     36         ringq_push(rqp,2);
     37         index -= 1;
     38     }
     39 
     40     ringq_display(rqp);
     41     
     42    return 0;
     43 }
    
    
    • 实验结果

    image-20210828223651731

    • 测试文件2
      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include "ringq.h"
      4 
      5 #define RQ_SIZE 5 
      6 
      7 int main(void)
      8 {
      9     int data_in = 0;
     10     int data_out = 0;
     11     int select = 0;
     12 
     13     int array[RQ_SIZE]={};
     14     RINGQ rq, *rqp;
     15     rqp = &rq;
     16 
     17     ringq_init(rqp, array, RQ_SIZE);
     18     ringq_display(rqp);
     19       
     42 
     43 loop:  puts("push or poll or quit? [i/o/q]");
     44     select = getchar();
     45     getchar();  //丢弃回车
     46     switch(select){
     47         case 'i':
     48             printf("The push data is:");
     49             scanf("%d",&data_in);
     50             getchar();  //丢弃回车
     51             ringq_push(rqp, data_in);
     52             break;
     53         case 'o':
     54             ringq_poll(rqp, &data_out);
     55             printf("%d poll successfull.\n",data_out);
     56             break;
     57         case 'q':
     58             
     59             return 0;
     60         default:
     61             printf("Wrong choice!enter i or o!\n");
     62     }
     63     ringq_display(rqp);
     64     printf("\n");
     65     goto loop;
     66 }
    
    • 实验结果

    image-20210828224608118

    image-20210828224656414

    3、附加满空标志位的环形队列

    • 数据结构
    typedef struct ringq{
       int head;
       int tail;
       int tag ;
       int size ;
       int space[RINGQ_MAX];
    
    }RINGQ;
    
    • 规则
      • 初始化状态: q->head = q->tail = q->tag = 0;
      • 队列为空:(q->head == q->tail) && (q->tag == 0)
      • 队列为满**: ((q->head == q->tail) && (q->tag == 1))**
      • 入队操作:如队列不满,则写入q->tail = (q->tail + 1) % q->size ;
      • 出队操作:如果队列不空,则从head处读出。下一个可读的位置在 q->head = (q->head + 1) % q->size
    • 头文件
    #ifndef __RINGQ_H__
    #define __RINGQ_H__
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #define QUEUE_MAX 20
    typedef struct ringq{
       int head;
       int tail;
       int tag ;
        int size ;
       int space[QUEUE_MAX];
    }RINGQ;
    
    
    extern int ringq_init(RINGQ * p_queue);
    extern int ringq_free(RINGQ * p_queue);
    extern int ringq_push(RINGQ * p_queue,int data);
    extern int ringq_poll(RINGQ * p_queue,int *p_data);
    #define ringq_is_empty(q) ( (q->head == q->tail) && (q->tag == 0))
    #define ringq_is_full(q) ( (q->head == q->tail) && (q->tag == 1))
    #define print_ringq(q) printf("ring head %d,tail %d,tag %d\n", q->head,q->tail,q->tag);
    #ifdef __cplusplus
    }
    #endif
    
    #endif
    
    • 实现代码
    #include <stdio.h>
    #include "ringq.h"
    
    int ringq_init(RINGQ * p_queue)
    {
       p_queue->size = QUEUE_MAX ;
       p_queue->head = 0;
       p_queue->tail = 0;
       p_queue->tag = 0;
       return 0;
    }
    
    int ringq_free(RINGQ * p_queue)
    {
        return 0;
    }
    //Write data to the queue
    int ringq_push(RINGQ * p_queue,int data)
    {
        print_ringq(p_queue);
        if(ringq_is_full(p_queue))
        {
            printf("ringq is full\n");
            return -1;
        }
        p_queue->space[p_queue->tail] = data;
        p_queue->tail = (p_queue->tail + 1) % p_queue->size ;
        if(p_queue->tail == p_queue->head)
        {
            p_queue->tag = 1;
        }
        return p_queue->tag ;
    }
    //Get data from the queue
    int ringq_poll(RINGQ * p_queue,int * p_data)
    {
        print_ringq(p_queue);
        if(ringq_is_empty(p_queue))
        {
            printf("ringq is empty\n");
            return -1;
        }
        *p_data = p_queue->space[p_queue->head];
        p_queue->head = (p_queue->head + 1) % p_queue->size ;
        if(p_queue->tail == p_queue->head)
        {
            p_queue->tag = 0;
        }
        return p_queue->tag ;
    }
    
    • 测试代码
    //请参考上节的test.c
    

    参考资料【转】环形队列理论(C语言) - 程序天空下的骆驼 - 博客园 (cnblogs.com)

    展开全文
  • FIFO轻量级环形缓冲管理器开源库--可嵌入式移植 blog: https://blog.csdn.net/Wekic/article/details/119046393
  • 免锁环形缓冲区实现单片机共享数据处理的同步.pdf

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,675
精华内容 9,870
关键字:

环形缓冲

友情链接: GPS_equasys_newdd.zip