Ring Buffer的高级用法(类似内核KFIFO)

Ring Buffer的高级用法(类似内核KFIFO)

  • 先上代码
    • 数据结构
    • 数据入队操作
    • 数据出队操作
    • 获取队列中可读数据的大小
    • 清空循环队列

先上代码

代码参考Linux内核Kfifo.

数据结构

数据结构中定义的缓存区大小一定要是2的n,当然也可以用动态分配来分配缓存区的大小,但是使用该高级用法一定要遵循分配的缓存区大小是2的n次方;至于为什么要这样先留个悬念,后面见原因;

#define MIN(a, b) (((a) < (b)) ? (a) : (b)) /* 取a和b中最小值 */

#define RING_BUFFER_SIZE       4096	//大小一定要为2的n次方才能使用该高级用法 

typedef struct {
    char buffer[RING_BUFFER_SIZE];  /* 缓冲区 ,大小一定要为2的n次方才能使用该高级用法 */
    unsigned int size;              /* 大小  注意要用unsigned类型*/
    unsigned int in;                /* 入口位置  注意要用unsigned类型*/
  	unsigned int out;               /* 出口位置  注意要用unsigned类型*/
} RingBuffer_t;

数据入队操作

先来看数据入队的操作,分以下几种情况分析:
1、ring_buf_p->in 、ring_buf_p->out均小于size;
只有开始使用循环队列的阶段才会是这种情况,先分析size = MIN(size, ring_buf_p->size - ring_buf_p->in + ring_buf_p->out);这句代码;代码补全为size = MIN(size, ring_buf_p->size - (ring_buf_p->in - ring_buf_p->out)); 由于ring_buf_p->in为入指针,ring_buf_p->out为出指针,则(ring_buf_p->in - ring_buf_p->out)即为循环缓存区已经被使用的大小,而 ring_buf_p->size - (ring_buf_p->in - ring_buf_p->out)即为循环缓存区剩余未使用的大小,与即将要写入的数据大小取二者中较小的,保证填入的数据不会出现越界或覆盖原有的数据。我们在看看len = MIN(size, ring_buf_p->size - (ring_buf_p->in & (ring_buf_p->size - 1)));这条语句,有的人可能不太理解 (ring_buf_p->in & (ring_buf_p->size - 1)))是什么意思,其实就是和ring_buf_p->in % ring_buf_p->size 的作用是一样的,就是取余;但是 (ring_buf_p->in & (ring_buf_p->size - 1)))的代码执行效率要比ring_buf_p->in % ring_buf_p->size高很多,在一下对实时性要求很高的使用场景下,代码的执行效率是要求很苛刻的;这是又要分两种情况讨论,第一种size小于等于ring_buf_p->size - (ring_buf_p->in & (ring_buf_p->size - 1));这说明循环缓存区的后半部分的未使用大小足够放下要写入的数据大小,数据只要一次就能完全写完进循环缓存区;第二种size大于ring_buf_p->size - (ring_buf_p->in & (ring_buf_p->size - 1));这说明循环缓存区的后半部分的未使用大小无法放下要写入的数据大小,数据只要分两次才能写入循环缓存区;第一次写入将后半部分剩余的缓存区大小使用完,第二次写入将剩余的未写入的数据大小从循环缓存区的首地址开始写入(这也就是循环缓冲区的作用,使用较小的实际物理内存实现了线性缓存);
2、ring_buf_p->in大于size 、ring_buf_p->out小于size;或 ring_buf_p->in 、ring_buf_p->out均大于于size;
这种情况才是体现改高级用法的时候,数据的写入和读取导致入指针域出指针的大小超过size的大小,先说明数据结构定义时为为什么要要求指针和大小的数据类型一定要为unsigned,因为在本高级用法中,没有用size的大小区取限制指针的大小的,入指针与出指针的大小均可以达到对于数据大小的最大值,而我们知道无符号类型的数据,大小超过最大值时,会出现溢出,导致数值又会从零开始变化,比如unsigned char, 254 + = 1,就是255 ,而255在计算机中的二进制存储为11111111,所以255+1,在计算机的存储就会变成100000000,而由于unsigned char只有八位,就会出现“溢出”的现象,所以255+1的结果为0,高级用法就是利用了无符号类型的数据特性。而至于为什么要使用大小要使用2的n次方的原因也是因为,所有的无符号数据类型的数值个数为2的n次方个,例如我们使用的指针类型为unsigned char, size的大小也使用2的8次方,也就是256,unsigned char的数据范围为0~255正好与数据中的每个字节一一对应。而当使用的size大小为2的7次方,也就是128时,size的也是可以整除unsigned char可以数据范围个数的,所以unsigned char的是任一个数对size可以取余都会落在每一个直接所对应的所有上。而且size使用2的n次方可以使用(ring_buf_p->in & (ring_buf_p->size - 1))取余这样的高级用法

unsigned int RingBufferPut(RingBuffer_t *ring_buf_p, void *buffer, hd_u32_t size)
{
   unsigned int len = 0;
   if(ring_buf_p == NULL ||  buffer == NULL) {
       return -1;
   }
   size = MIN(size, ring_buf_p->size - ring_buf_p->in + ring_buf_p->out);
   /* first put the data starting from fifo->in to buffer end */
   len  = MIN(size, ring_buf_p->size - (ring_buf_p->in & (ring_buf_p->size - 1)));
   memcpy(ring_buf_p->buffer + (ring_buf_p->in & (ring_buf_p->size - 1)), buffer, len);
   /* then put the rest (if any) at the beginning of the buffer */
   if (size - len > 0) {
       memcpy(ring_buf_p->buffer, (char *)buffer + len, size - len);
   }
   ring_buf_p->in += size;
   return size;
}

数据出队操作

先来看数据入队的操作,参考入队的解释说明

unsigned int RingBufferGet(RingBuffer_t *ring_buf_p, void *buffer, hd_u32_t size)
{
  unsigned int len = 0;
  if(ring_buf_p == NULL || buffer == NULL) {
  	return -1;
  }
  size  = MIN(size, ring_buf_p->in - ring_buf_p->out);
  /* first get the data from fifo->out until the end of the buffer */
  len = MIN(size, ring_buf_p->size - (ring_buf_p->out & (ring_buf_p->size - 1)));
  memcpy(buffer, ring_buf_p->buffer + (ring_buf_p->out & (ring_buf_p->size - 1)), len);
  /* then get the rest (if any) from the beginning of the buffer */
  if (size - len > 0) {
      memcpy((char *)buffer + len, ring_buf_p->buffer, size - len);
  }
  ring_buf_p->out += size;
  return size;
}

获取队列中可读数据的大小

unsigned int RingBufferLen(const RingBuffer_t *ring_buf_p)
{
   return (ring_buf_p->in - ring_buf_p->out);
}

清空循环队列

void RingBufferClear(RingBuffer_t *ring_buf_p)
{
  ring_buf_p->in = 0;
  ring_buf_p->out = 0;
}

你可能感兴趣的:(C语言,数据结构)