环形缓存区工作原理
环形缓冲区是固定大小的缓冲区,工作原理就像内存是连续的且可循环。在生成和使用内存时,不需要将原来的数据全部清理掉,只要调整head/tail指针即可。当添加数据时,head指针前进。当使用数据时,tail指针向前移动。当到达缓冲区的尾部时,指针又回到缓冲区的起始位置。
优势:当有大量数据但不需要全部存储的情况下,计算机在处理数据时会先处理先来的,处理完之后会把数据释放掉,再继续处理下一个。那么已经处理的数据的内存就会被浪费掉。因为后来的数据只能往后排队,如果将剩余的数据都往前移动一次,效率便会大大降低。因此环形队列就出现了。核心是申请一段内存,通过特殊方法让其首尾相连,形成一个闭环的内存地址。这样既可以继续处理源源不断的数据,又不用再去申请新的内存空间暂存新的数据,效率大大提高。
劣势:环形缓冲区最重要的一个特点是写的速度和读的速度匹配性问题,环形缓冲区一般是将数据先写入缓冲区内,然后有个写的指针位置,但是不能超过读的位置,因为数据还未被读取,便写入数据,就会造成数据还未被读取便被新的数据覆盖,造成数据的丢失。读数据同样原理,读的指针不能超过写的位置,这样就会读到之前读过的数据,造成数据的重复。空间如果设置的太大,会造成内存的浪费,有内存一直处于空闲状态,如果空间设置到的太小,可能会造成读的速度快于写的速度,导致读的过程中会有短暂的等待时间,造成效率不是最高。
1、写入数据<缓冲区
2、写入数据>缓冲区
代码:
RingBuffer.c
#include "RingBuffer.h"
static UINT16 validlen; //已使用的数据长度
static UINT8* pHead = NULL; //环形缓冲区首地址
static UINT8* pTail = NULL; //环形缓存区尾地址
static UINT8* pValid = NULL; //已使用缓冲区的首地址
static UINT8* pValidTail = NULL; //已使用缓冲区的尾地址
//malloc申请空间
void initRingbuffer(void)
{
if(pHead == NULL)
{
pHead = (char*) malloc(BUFFER_SIZE);
}
pValid = pValidTail = pHead;
pTail = pHead + BUFFER_SIZE;
validlen = 0;
}
//释放环形缓冲区
void releaseRingbuffer(void)
{
if(pHead != NULL)
free(pHead);
pHead = NULL;
}
/*************************************************************/
/******功能:初始化指定的环形缓冲区 *****/
/******param:@buffer:定义静态缓冲区数组的首地址
param:@size:该缓冲区的大小 ******/
/************************************************************/
void InitRingBuffer(UINT8* buffer, UINT16 size)
{
if(pHead == NULL)
{
pHead = buffer;
}
pValid = pValidTail = pHead;
pTail = pHead + size;
validlen = 0;
}
/***********************************************************/
/******功能:向缓冲区写入数据 *****/
/******param:@buffer 写入的数据指针
@len 写入的数据长度 ******/
/******return:-1 写入的数据长度过大 -2:缓冲区未初始化 *****/
/***********************************************************/
int writeRingbuffer(UINT8* buffer, UINT32 len)
{
UINT16 len1, len2, movelen;
if(len > BUFFER_SIZE)
{
return -1;
}
if(pHead == NULL)
{
return -2;
}
assert(buffer);
//将要写入的数据copy到pVaildTaill处
if(pValidTail+len > pTail)
{
len1 = pTail - pValidTail; //放到尾部
len2 = len - len1; //尾部放不下的位置,从头开始放
memcpy(pValidTail, buffer, len1);
memcpy(pHead, buffer + len1, len2);
pValidTail = pHead + len2; //新的有效数据
}else
{
memcpy(pValidTail, buffer, len);
pValidTail += len; //新的有效数据区结尾
}
//重新计算已使用区的起始位置
if(validlen + len > BUFFER_SIZE)
{
movelen = validlen + len - BUFFER_SIZE;
if(pValid + movelen > pTail) //需要分成两段
{
len1 = pTail - pValid;
len2 = movelen - len1;
pValid = pHead + len2;
}else
{
pValid += movelen;
}
validlen = BUFFER_SIZE;
}else
{
validlen += len;
}
return 0;
}
/*************************************************************/
/******功能:从缓冲区读取指定长度的数据 *****/
/******param:@buffer 接收读取数据的指针
@len 读取的数据长度 ******/
/******return:-1 缓冲区未初始化 >0:读取的有效数据大小 *****/
/*************************************************************/
int readRingbuffer(UINT8* buffer, UINT32 len)
{
UINT16 len1, len2;
if(pHead == NULL)
{
return -1;
}
assert(buffer);
if(len > validlen)
{
len = validlen;
}
if(validlen == 0)
{
return 0;
}
if(pValid + len > pTail) //需要分成两段copy
{
len1 = pTail - pValid;
len2 = len - len1;
memcpy(buffer, pValid, len1); //第一段
memcpy(buffer + len1, pHead, len2); //第二段 ,绕到整个存储区的开头
pValid = pHead + len2; //更新已使用缓冲区的起始
}else
{
memcpy(buffer, pValid, len);
pValid += len; //更新已使用缓冲区的起始
}
validlen -= len; //更新已使用缓冲区的长度
return len;
}
/*************************************************************/
/******功能:获取已使用的缓冲区数据长度 *****/
/******param:void ******/
/******return:已使用缓冲区的数据长度 *****/
/************************************************************/
UINT32 getRingbufferValidLen(void)
{
return validlen;
}
Ringbuffer.h
#ifndef _RINGBUFFER_H_
#define _RINGBUFFER_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
typedef unsigned char UINT8;
typedef unsigned short int UINT16;
typedef unsigned int UINT32;
#define BUFFER_SIZE 5
void initRingbuffer(void);
int writeRingbuffer(UINT8* buffer, UINT32 len);
int readRingbuffer(UINT8* buffer, UINT32 len);
UINT32 getRingbufferValidLen(void);
void releaseRingbuffer(void);
void InitRingBuffer(UINT8* buffer, UINT16 size);
#endif
main.c
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "RingBuffer.h"
int main()
{
UINT8 c;
UINT32 readLen,i;
UINT8 readBuffer[5] = {0};
UINT8 buffer[5] = {0};
InitRingBuffer(buffer,5);//初始化环形缓冲区
printf("Please enter a line>\n");
do{
c = getchar();
putchar(c);
switch(c)
{
case 'Q':
goto exit;
break;
case 'R':
readLen = readRingbuffer(readBuffer,5);
printf("readRingbuffer len:%d\n",readLen);
if(readLen > 0)
{
printf("readRingbuffer:");
for(i = 0; i < readLen; i++)
{
printf("%c ",(UINT8)readBuffer[i]);
}
printf("\n");
}
break;
default :
if(c != '\n')
writeRingbuffer((UINT8*)&c,1);
break;
}
}while(1);
exit:
releaseRingbuffer();
printf("\nexit!\n");
return 0;
}