文字和图片参考和来自这些文章:
大疆嵌入式软件编程题==找鞍点_已知循环缓冲区是一个可以无限循环读写的缓冲区,当缓冲区满了还继续写的话就会覆_一禅的师兄的博客-CSDN博客
ring buffer,一篇文章讲透它? - 知乎 (zhihu.com)
1 概述
1.1 什么是ringbuff
ring buffer称作环形缓冲区,也称作环形队列(circular queue),是一种用于表示一个固定尺寸、头尾相连的缓冲区的数据结构,适合缓存数据流。
1.2 宏定义
这个宏定义用于计算向下对齐的地址。它接收两个参数:size
和 align
。size
是要对齐的内存大小,align
是所需的对齐大小。这个宏返回一个值,该值是 size
向下对齐到 align
边界后的地址。
#define DATA_ALIGN_DOWN(size, align) ((size) & ~((align) - 1))
例如,假设我们有一个 32 位的系统,并且我们需要将数据对齐到 64 位的边界。我们可以使用 DATA_ALIGN_DOWN
宏来计算数据对齐后的地址:
#define DATA_ALIGN_DOWN(size, align) ((size) & ~((align) - 1))
int data[10];
int *aligned_data = DATA_ALIGN_DOWN(data, 8);
在这个例子中,data
数组的大小为 10 个元素,我们需要将其对齐到 8 字节边界。DATA_ALIGN_DOWN
宏返回 data
数组中第一个元素的地址,即 &data[0]
。由于我们需要 8 字节对齐,我们将返回的地址减去 4 个字节,以获得正确的对齐地址:
int *aligned_data = &data[0] - 4;
2 原理
2.1 具体怎么做?
一般来说,环形缓冲区进行读写操作,最少需要4个信息:
🐋 ① 在内存中的实际开始位置
🐋 ② 在内存中的实际结束位置
🐋 ③ 在缓冲区中进行写操作时的写索引值
🐋 ④ 在缓冲区中进行读操作时的读索引值
4个信息的作用:
🐳 ① 缓冲区的开始位置和缓冲区的结束位置(或空间大小)实际上定义了环形缓冲区的实际逻辑空间和大小
🐳 ② 读索引和写索引标记了缓冲区进行读操作和写操作时的具体位置
图1:当环形缓冲区为空时,读索引和写索引指向相同的位置(因为是环形缓冲区,可以出现在任何位置);
图2:当向缓冲区写入一个元素时,元素A被写入到写索引当前所指向的位置,接着写索引加1,指向下一个位置;
图3:当再写入一个元素B时,元素B继续被写入到写索引当前所指向的位置,接着写索引加1,指向下一个位置;
图4:当接着写入C、D、E、F、G五个元素后,缓冲区就满了,这时写索引和读索引指向同一个位置(和缓冲区为空时一样);
图5:当从缓冲区中读出一个元素A时,读索引当前所在位置的元素被读出,接着读索引加1,指向下一个位置;
图6:继续读出元素B时,还是读索引当前所在位置的元素被读出,接着读索引加1,指向下一个位置
2.2 环形缓冲区和镜像指示位
2.2.1 在缓冲区满的时候写数据,有两种策略可以使用
缓冲区变满在环形缓冲区(ring buffer)中会实际发生,一般会有两种处理策略:
🐞① 覆盖老数据
🐞② 抛出“异常”
两种策略如何选择要结合具体的应用场景:
🦥选择第一种策略:如音/视频流中,丢掉一些数据不要紧
🦝选择第二种策略:在任务间通信的,要严格保证数据正确传输
2.2.2 读数据时,一定要读出缓冲区中最老的数据
🦉① 环形缓冲区(ring buffer)也是FIFO类型的数据结构,需要满足先进先出的原则
🦉② 写就相当于进,读就相当于出
🦉③ 读数据时,一定要保证读最老的数据
一般读数据时,不会有问题。但有一种场景需要小心,看如下两张图
图一所示:环形缓冲区的大小为七,缓冲区中已经存储了五个元素,7,8,9,3,4
图二所示:再向缓冲区中写入三个元素A,B,C,因为剩余空间为2 了,所以要写入这三个元素肯定会覆盖掉一个元素。也就是元素7被覆盖为元素C,读索引不再停留在元素7处,而是在元素8处。此时元素8变为最老的元素。
这个例子表明:当缓冲区是满的,继续写入元素(覆盖),除了写索引要变,读索引也要跟着变,保证读索引一定是指向缓冲区中最老的元素。
【举个例子】:
🐞 这是一个空间大小为7的环形缓冲区,其中底部的单线箭头表示“头尾相连”形成一个环形地址空间
🐞 将1写入缓冲区中部(对于环形缓冲区来说,最初的写入位置在哪里是无关紧要的)
🐞 再写入两个元素,分别是2
和3
,这两个元素被追加到1
之后
🐞 读出两个元素,那么环形缓冲区中最老的两个元素将被读出(先进先出原则)
🐞 紧接着,向缓冲区中写入六个元素4、5、6、7、8、9
,这时缓冲区会被装满
🐞 如果缓冲区是满的,又要写入新的数据,这时有两种策略:一种是覆盖掉最老的数据,也就是将老数据丢掉;另一种是返回错误码或者抛出异常。来看策略一,例如,这时写入两个元素A
和B
,就会覆盖掉3
和4
🐞 读出两个元素,就不是3
和4
而是5
和6
(5
和6
这时最老),3
和4
已经被A
和B
覆盖掉
实现环形缓冲区(ring buffer)需要注意的点:
🦝1.在缓冲区满的时候写数据,有两种策略可以使用: ① 覆盖掉老数据 ② 抛出异常
🦝2.读数据时,一定要读出缓冲区中最老的数据(缓冲区中数据满足FIFO特性)
🦝3.判断缓冲区是满的
🦝4.实现一个线性地址空间的循环读写
2.2.3 怎样来判断缓冲区是满的
缓冲区是满还是空,都有可能出现读索引与写索引指向同一位置
如何判断缓冲区是满还是空,在环形缓冲区中是一个重点问题,本文重点讲解镜像指示位
镜像指示位:缓冲区的长度如果是n,逻辑地址空间则为0至n-1;那么,规定n至2n-1为镜像逻辑地址空间。本策略规定读写指针的地址空间为0至2n-1,其中低半部分对应于常规的逻辑地址空间,高半部分对应于镜像逻辑地址空间。当指针值大于等于2n时,使其折返(wrapped)到ptr-2n。使用一位表示写指针或读指针是否进入了虚拟的镜像存储区:置位表示进入,不置位表示没进入还在基本存储区。
在读写指针的值相同情况下,如果二者的指示位相同,说明缓冲区为空;如果二者的指示位不同,说明缓冲区为满。这种方法优点是测试缓冲区满/空很简单;不需要做取余数操作;读写线程可以分别设计专用算法策略,能实现精致的并发控制。缺点是读写指针各需要额外的一位作为指示位。
如果缓冲区长度是2的幂,则本方法可以省略镜像指示位。如果读写指针的值相等,则缓冲区为空;如果读写指针相差n,则缓冲区为满,这可以用条件表达式(写指针==(读指针异或缓冲区长度))来判断。
----(来自百度百科)
【总结】
>>镜像指示位(有关读写指针)
🍇如何判别缓冲区判空/满?
🦝① 在读写指针的值相同情况下,如果二者的指示位相同,说明缓冲区为空;
🦝② 如果二者的指示位不同,说明缓冲区为满
逻辑:若写指针超出了读指针n位,在普通情况下,读写指针重合,但在这种情况下,写指针在镜像空间的读指针+n位,不和读指针重合
🍓优点:
🦝① 测试缓冲区满/空很简单;不需要做取余数操作;
🦝② 读写线程可以分别设计专用算法策略,能实现精致的并发控制
🍄缺点:读写指针各需要额外的一位作为指示位
🐞 前提:如果缓冲区长度是2的幂,则本方法可以省略镜像指示位
🦝① 如果读写指针的值相等,则缓冲区为空;
🦝② 如果读写指针相差n,则缓冲区为满;
🦝③ 可用 (写指针 == (读指针 异或 缓冲区长度))来判断
写指针: 1011
读指针: 0011
缓冲区长度: 1000
异或: 1011
>>区别 读写指针 和 读写索引
🦝① 读写指针的范围是[0,2n-1]
🦝② 读索引和写索引的范围是[0,n-1],其必须和缓冲区的实际逻辑空间一致
🦝③ 读指针和读索引,写指针和写索引的转换关系:
读索引 = 读指针 % 缓冲区长度
写索引 = 写指针 % 缓冲区长度
其中%号,是求余运算符,但是如果缓冲区长度是2的幂,那么求余运算可以等价的转换为如下的按位与运算:
读索引 = 读指针&(缓冲区长度 - 1)
写索引 = 写指针&(缓冲区长度 - 1)
🍇思考:为什么环形缓冲区大小必须是2的幂?
使用2的幂的优势:
size = 32;
bin(size) => '00100000'
mask = size - 1;
bin(mask)=>'00011111'
使用按位and应用此掩码,随着索引的增长,我们仅隔离包含0-31范围内的数字的位
index = 4
bin(index & mask)=>'00000100' (4)
index = 32
bin(index & mask)=>'00000000' (0)
index = 33
bin(index & mask)=>'00000001' (1)
index = 64
bin(index & mask)=>'00000000' (0)
index = 65
bin(index & mask)=>'00000001' (1)
🍓【总结】这种方法不需要比较,不需要分支,并且是安全的(结果索引始终在范围内)。它还有一个额外的好处,那就是不会丢弃信息;但仍然保留了索引在逻辑上是65 的信息(事实证明这非常有用)
🍄 【效率分析】:按位与运算效率要比求余运算高得多,在Linux内核中将缓冲区长度扩展为2的幂长度随处可见,都是为了按位与操作代替求余操作。
🍄【归纳】为了判断缓冲区是否为空或者满,镜像指示位策略引入了两个布尔变量(指示位),来分别标记读指针或者写指针是否进入了镜像空间[n,2n-1],在读写指针的值相同情况下,如果二者的指示位相同,说明缓冲区为空;如果二者的指示位不同,说明缓冲区为满。但如果缓冲区的长度为2的幂,则可以省略镜像指示位。如果读写指针的值相等,则缓冲区为空;如果读写指针相差n,则缓冲区为满。
三种可能出现的情形:
情形一中,开始缓冲区中有两个元素1、2,接着继续写入A、B、C三个元素。就是说从写索引到缓冲区结束位置这一段空间能容纳全部所写入数据
情形二中,开始缓冲区有两个元素1、2,接着继续写入A、B、C三个元素。可以看出写索引到缓冲区结束位置还可以容纳2个元素,也就是说A、B两个元素可以写入从写索引到缓冲区结束位置这一段,而C只能回环到缓冲区的开头位置。就是说从写索引到缓冲区结束位置这一段空间无法全部容纳所写入数据,写索引需要回环到缓冲区开头,写入剩下的数据
情形三中,描述了该过程,开始缓冲区中有两个元素1、2,接着继续写入A、B、C、D、E、F六个元素。此时元素 1 被覆盖掉,写索引和读索引都指向元素 2 . 如果发生了元素覆盖,那缓冲区一定会变满,read_index和write_index会相等
2.2.4 完整代码
ringbuffer.cpp
#include <iostream>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <cmath>
#define DATA_ALIGN_SIZE 4
#define DATA_ALIGN_DOWN(size, align) ((size) & ~((align) - 1))
using namespace std;
typedef struct ringbuff{
char* buff;
unsigned int size;
unsigned int rdId;//读索引readIndex
unsigned int wrId;//写索引writeIndex
uint16_t read_mirror : 1;
uint16_t write_mirror : 1;
}ringbuff;
void Init(ringbuff* rb,char* buff,unsigned int size) {
memset(rb,0,sizeof(rb));
rb->size = DATA_ALIGN_DOWN(size, DATA_ALIGN_SIZE);
// rb->size = size;
rb->buff = buff;
rb->rdId = 0;
rb->wrId = 0;
rb->write_mirror = rb->read_mirror = 0;
}
#define RINGBUFF_EMPTY 0
#define RINGBUFF_FULL 1
#define RINGBUFF_HALFFULL 2
// 获得缓冲区所剩空闲个数:缓冲区长度 - 缓冲区存放数据的空间个数
#define rb_free_size(rb) ((rb)->size - rb_data_size(rb))
static inline int32_t rb_state_get(ringbuff* rb) {
if(rb->rdId == rb->wrId) {
if(rb->read_mirror == rb->write_mirror)
return RINGBUFF_EMPTY;
else
return RINGBUFF_FULL;
}
else
return RINGBUFF_HALFFULL;
}
// 获取缓冲区所存储数据长度
int16_t rb_data_size(ringbuff* rb) {
switch (rb_state_get(rb))
{
case RINGBUFF_EMPTY:
return 0;
case RINGBUFF_FULL:
return rb->size;
case RINGBUFF_HALFFULL:
if(rb->read_mirror == rb->write_mirror) {
// return abs(int(rb->wrId - rb->rdId));
return abs(int(rb->wrId - rb->rdId));
} else {
// return rb->size - rb->rdId - rb->wrId;
if(rb->rdId > rb->wrId)
return rb->size - (rb->rdId - rb->wrId);
else
return rb->wrId - rb->rdId;
}
}
// never reach here
return rb->size;
};
void write(ringbuff* rb,char * buf,unsigned int strlen)
{
unsigned int pos=rb->wrId;//记录我们写指针的偏移
unsigned int tmpPos=pos;//记录偏移位置
int flag = 0;
unsigned int tlen = strlen;
int empty = rb_free_size(rb);
int data = rb_data_size(rb);
while(pos+strlen>rb->size){//判断我们写的字节数是否大于整个buffer长度
//cout<<"进入循环: "<<endl;
memcpy(rb->buff+pos,buf,rb->size-pos);//将我们的内容直接拷贝到偏移位置
buf +=(rb->size-pos); //获取要拷贝的buf地址
strlen -=rb->size-pos; //计算多出来的长度
pos=0;
flag = 1;
}
memcpy(rb->buff+pos,buf,strlen);//将多出来的长度的内容搬到基址开始,从我们的buf中继续写过来,实现循环
rb->wrId=pos+strlen; //移动我们的写指针
// 方法二
rb->wrId = rb->wrId & (rb->size - 1);
int mirrorFlag = 0;
if(flag && !rb->write_mirror || (rb->write_mirror==1 && rb->read_mirror==1)) {
mirrorFlag = 1;
}
if(flag && !pos && mirrorFlag|| rb->wrId == 0) {
// cout<<"取反~"<<endl;
if(rb->write_mirror==1 && rb->read_mirror==1) {
rb->read_mirror = 0;
}
if(rb->write_mirror==0 && rb->read_mirror==0) {
rb->write_mirror = ~rb->write_mirror;
}
}
int modify_flag = 0;
if(rb->wrId < rb->rdId && tlen<rb->size && empty!=rb->size){
// cout<<"进入判断:~"<<endl;
modify_flag = 1;
}
int t = rb->size-1-(tlen+rb->wrId);
// cout<<"rb->size-1-(tlen+rb->wrId) : "<<t<<endl;
if(modify_flag && t==0) {
// cout<<"modify_flag: "<<modify_flag<<endl;
// cout<<"打印:"<<tmpPos-rb->wrId<<endl;
if(!(rb->wrId < rb->rdId && tlen <empty)) {
rb->rdId = rb->wrId + t;
}
modify_flag = 0;
}
if(rb->wrId + (empty-tlen) == rb->rdId) {
// cout<<"==="<<endl;
modify_flag = 0;
}
if(rb->rdId < rb->wrId && rb->write_mirror || modify_flag) {
rb->rdId = rb->wrId;
}
}
int read(ringbuff* rb,char* srcbuff,unsigned int len) {
unsigned int pos = rb->rdId;
unsigned int tlen = len;
int flag = 0;
// empty buffer 报错!!
if(!rb_data_size(rb)) {
cout<<"empty buffer"<<endl;
return 0;
}
if(len > rb_data_size(rb)) {
len = rb_data_size(rb);
cout<<"已超出缓冲区大小,只可读取"<<len<<"个字节"<<endl;
}
while(pos + len > rb->size) {
memcpy(srcbuff,rb->buff + pos,rb->size - pos);
srcbuff += (rb->size - pos);
len -= (rb->size - pos);
pos = 0;
flag = 1;
}
memcpy(srcbuff,rb->buff + pos,len);
rb->rdId = pos + len;
rb->rdId = rb->rdId & (rb->size - 1);
if(flag && !pos || rb->rdId == 0) {
rb->read_mirror = ~rb->read_mirror;
}
if(rb->rdId == rb->wrId) {
rb->read_mirror = rb->write_mirror = 0;
}
}
void display(ringbuff* rb) {
for(int i=0;i<rb->size;i++){
cout<<rb->buff[i];
}
cout<<endl;
}
void readprint(ringbuff* rb,int len) {
cout<<endl;
char buff[20] = "";
read(rb, buff, len);
int i = 0;
cout<<"读出:";
while(buff[i]!='\0') {
cout<<buff[i++];
}
cout<<endl;
cout<<"read_index: "<<rb->rdId<<endl;
cout<<"write_index: "<<rb->wrId<<endl;
cout<<"rb->read_mirror: "<<rb->read_mirror<<endl;
cout<<"rb->write_mirror: "<<rb->write_mirror<<endl;
cout<<"rb_state_get: "<<rb_state_get(rb)<<endl;
cout<<"rb_data_size: "<<rb_data_size(rb)<<endl;
cout<<"rb_free_size: "<<rb_free_size(rb)<<endl;
cout<<endl;
}
void writeprint(ringbuff* rb,char* p,int len) {
cout<<"写入:";
for(int i=0;i<len-1;i++) {
cout<<*(p+i);
}
cout<<endl;
write(rb,p,len-1);
display(rb);
cout<<"read_index: "<<rb->rdId<<endl;
cout<<"write_index: "<<rb->wrId<<endl;
cout<<"rb->read_mirror: "<<rb->read_mirror<<endl;
cout<<"rb->write_mirror: "<<rb->write_mirror<<endl;
cout<<"rb_state_get: "<<rb_state_get(rb)<<endl;
cout<<"rb_data_size: "<<rb_data_size(rb)<<endl;
cout<<"rb_free_size: "<<rb_free_size(rb)<<endl;
cout<<endl;
}
int main() {
ringbuff* rb = (ringbuff*)malloc(sizeof(ringbuff));
char init_str[8] = "0000000";
int size = sizeof(init_str);
Init(rb,init_str,size);
// char p[] = "123456789A";
char p[] = "123456789A12345678";
// char p[] = "12345678";
int len = sizeof(p) / sizeof(char);
writeprint(rb,p,len);
readprint(rb,7);
readprint(rb,9);
readprint(rb,9);
char p1[] = "HEHEDA";
len = sizeof(p1) / sizeof(char);
writeprint(rb,p1,len);
char p2[] = "Tom";
len = sizeof(p2) / sizeof(char);
writeprint(rb,p2,len);
char p3[] = "abcdefgh";
len = sizeof(p3) / sizeof(char);
writeprint(rb,p3,len);
readprint(rb,6);
readprint(rb,3);
char p4[] = "abcdefgh";
len = sizeof(p4) / sizeof(char);
writeprint(rb,p4,len);
char p5[] = "kaixinma";
len = sizeof(p5) / sizeof(char);
writeprint(rb,p5,len);
readprint(rb,6);
char p6[] = "handou";
len = sizeof(p6) / sizeof(char);
writeprint(rb,p6,len);
readprint(rb,6);
char p7[] = "xiaohua";
len = sizeof(p7) / sizeof(char);
writeprint(rb,p7,len);
char p8[] = "houlai";
len = sizeof(p8) / sizeof(char);
writeprint(rb,p8,len);
char p9[] = "small";
len = sizeof(p9) / sizeof(char);
writeprint(rb,p9,len);
char p10[] = "bigboy";
len = sizeof(p10) / sizeof(char);
writeprint(rb,p10,len);
char p11[] = "yaoguai";
len = sizeof(p11) / sizeof(char);
writeprint(rb,p11,len);
char p12[] = "halemu";
len = sizeof(p12) / sizeof(char);
writeprint(rb,p12,len);
readprint(rb,7);
char p13[] = "kabola";
len = sizeof(p13) / sizeof(char);
writeprint(rb,p13,len);
char p14[] = "skuraxi";
len = sizeof(p14) / sizeof(char);
writeprint(rb,p14,len);
char p15[] = "xiaola";
len = sizeof(p15) / sizeof(char);
writeprint(rb,p15,len);
readprint(rb,7);
char p16[] = "bobonihao";
len = sizeof(p16) / sizeof(char);
writeprint(rb,p16,len);
char p17[] = "sha";
len = sizeof(p17) / sizeof(char);
writeprint(rb,p17,len);
readprint(rb,6);
char p18[] = "guale";
len = sizeof(p18) / sizeof(char);
writeprint(rb,p18,len);
readprint(rb, 8);
char p19[] = "chaoshan";
len = sizeof(p19) / sizeof(char);
writeprint(rb,p19,len);
char p20[] = "huabao";
len = sizeof(p20) / sizeof(char);
writeprint(rb,p20,len);
}
测试过程:
heheda@heheda:~/Linux/RT-Thread$ g++ ringbuffer.cpp -o app
heheda@heheda:~/Linux/RT-Thread$ ./app
写入:123456789A12345678
78123456
read_index: 2
write_index: 2
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
读出:1234567
read_index: 1
write_index: 2
rb->read_mirror: 1
rb->write_mirror: 1
rb_state_get: 2
rb_data_size: 1
rb_free_size: 7
已超出缓冲区大小,只可读取1个字节
读出:8
read_index: 2
write_index: 2
rb->read_mirror: 0
rb->write_mirror: 0
rb_state_get: 0
rb_data_size: 0
rb_free_size: 8
empty buffer
读出:
read_index: 2
write_index: 2
rb->read_mirror: 0
rb->write_mirror: 0
rb_state_get: 0
rb_data_size: 0
rb_free_size: 8
写入:HEHEDA
78HEHEDA
read_index: 2
write_index: 0
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 2
rb_data_size: 6
rb_free_size: 2
写入:Tom
TomEHEDA
read_index: 3
write_index: 3
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
写入:abcdefgh
fghabcde
read_index: 3
write_index: 3
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
读出:abcdef
read_index: 1
write_index: 3
rb->read_mirror: 1
rb->write_mirror: 1
rb_state_get: 2
rb_data_size: 2
rb_free_size: 6
已超出缓冲区大小,只可读取2个字节
读出:gh
read_index: 3
write_index: 3
rb->read_mirror: 0
rb->write_mirror: 0
rb_state_get: 0
rb_data_size: 0
rb_free_size: 8
写入:abcdefgh
fghabcde
read_index: 3
write_index: 3
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
写入:kaixinma
nmakaixi
read_index: 3
write_index: 3
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
读出:kaixin
read_index: 1
write_index: 3
rb->read_mirror: 1
rb->write_mirror: 1
rb_state_get: 2
rb_data_size: 2
rb_free_size: 6
写入:handou
umahando
read_index: 1
write_index: 1
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
读出:mahand
read_index: 7
write_index: 1
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 2
rb_data_size: 2
rb_free_size: 6
写入:xiaohua
uxiaohua
read_index: 0
write_index: 0
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
写入:houlai
houlaiua
read_index: 6
write_index: 6
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
写入:small
alllaism
read_index: 3
write_index: 3
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
写入:bigboy
yllbigbo
read_index: 1
write_index: 1
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
写入:yaoguai
yyaoguai
read_index: 0
write_index: 0
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
写入:halemu
halemuai
read_index: 6
write_index: 6
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
读出:aihalem
read_index: 5
write_index: 6
rb->read_mirror: 1
rb->write_mirror: 1
rb_state_get: 2
rb_data_size: 1
rb_free_size: 7
写入:kabola
bolamuka
read_index: 5
write_index: 4
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 2
rb_data_size: 7
rb_free_size: 1
写入:skuraxi
axiaskur
read_index: 3
write_index: 3
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
写入:xiaola
axixiaol
read_index: 1
write_index: 1
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
读出:xixiaol
read_index: 0
write_index: 1
rb->read_mirror: 1
rb->write_mirror: 1
rb_state_get: 2
rb_data_size: 1
rb_free_size: 7
写入:bobonihao
aoobonih
read_index: 2
write_index: 2
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
写入:sha
aoshanih
read_index: 5
write_index: 5
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
读出:nihaos
read_index: 3
write_index: 5
rb->read_mirror: 1
rb->write_mirror: 1
rb_state_get: 2
rb_data_size: 2
rb_free_size: 6
写入:guale
leshagua
read_index: 3
write_index: 2
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 2
rb_data_size: 7
rb_free_size: 1
已超出缓冲区大小,只可读取7个字节
读出:haguale
read_index: 2
write_index: 2
rb->read_mirror: 0
rb->write_mirror: 0
rb_state_get: 0
rb_data_size: 0
rb_free_size: 8
写入:chaoshan
anchaosh
read_index: 2
write_index: 2
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0
写入:huabao
anhuabao
read_index: 0
write_index: 0
rb->read_mirror: 0
rb->write_mirror: 1
rb_state_get: 1
rb_data_size: 8
rb_free_size: 0