C语言共享内存实现CyclicBuffer循环缓冲区

本文最后更新于:2 个月前

完整代码详见GitHub CyclicBuffer

什么是循环缓冲区

循环缓冲区通常应用在模块与模块之间的通信,可以减少程序挂起的时间,节省内存空间。

如图所示,蓝色箭头表示读取指针,红色表示写入指针。写入指针可以在缓冲区有剩余空间时不中断地写入数据,读取指针可以在循环缓冲区有数据时不停读取。

如何设计循环缓冲区

为了方便两个进程之间的通信,我们在共享内存中创建循环缓冲区。基本原理如图:

结构体定义

1
2
3
4
5
6
7
typedef struct CyclicBuffer
{
uint8_t buf[CYCBUFFSIZ]; //缓冲区
uint8_t read; //读指针
uint8_t write; //写指针
uint32_t valid_size; //已写入数据数
} CyCBuf;

写入数据

1
2
3
4
5
6
7
8
9
void cycbuff_write(CyCBuf *cycbuff, uint8_t ch)
{
while (cycbuff_isfull(cycbuff))
;
cycbuff->buf[cycbuff->write] = ch;
cycbuff->write++;
cycbuff->write %= CYCBUFFSIZ;
cycbuff->valid_size++;
}

写入数据前,要检查缓冲区是否已满,如果已满就得挂起等待。直到缓冲区有空间再进行写入。

写入指针每次写完向后偏移一位,valid_size记录当前缓冲区中有效数据个数。

读取数据

1
2
3
4
5
6
7
8
9
10
11
uint8_t cycbuff_read(CyCBuf *cycbuff)
{
uint8_t ch;
while (cycbuff_isempty(cycbuff))
;
ch = cycbuff->buf[cycbuff->read];
cycbuff->read++;
cycbuff->read %= CYCBUFFSIZ;
cycbuff->valid_size--;
return ch;
}

读取数据前,要检查缓冲区是否为空,如果为空就要挂起等待。

判断空

1
2
3
4
5
6
bool cycbuff_isempty(CyCBuf *cycbuff)
{
if (cycbuff->valid_size == 0)
return true;
return false;
}

判断满

1
2
3
4
5
6
bool cycbuff_isfull(CyCBuf *cycbuff)
{
if (cycbuff->valid_size == CYCBUFFSIZ)
return true;
return false;
}

本次实验中,为了方便期间,用valid_size保存有效数据个数,没有用读写指针是否重合来判断,这就无需再考虑读写指针重合时,是空还是满。

数据收发流程

服务端-写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void server(CyCBuf *cycbuff, SHMS *shms)
{
cycbuff_init(cycbuff);
while (1)
{
puts("Enter Message: ");
uint8_t ch[BUFFERSIZE];
fgets(ch, BUFFERSIZE, stdin);
for (size_t i = 0; ch[i] != '\n' && i < BUFFERSIZE; i++)
{
cycbuff_write(cycbuff, ch[i]);
}
cycbuff_write(cycbuff, '\n');
}
exit(0);
}

SHMS *shms为共享内存相关数据,有关共享内存的使用可以参考进程间通信(IPC)之共享内存(SharedMemory)

客户端-读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void client(CyCBuf *cycbuff, SHMS *shms)
{
printf("Server operational: shm id is %d\n", shms->shmid);
while (1)
{
uint8_t ch;
puts("Recv Message: ");
while (1)
{
ch = cycbuff_read(cycbuff);
if (ch == '\n')
{
printf("\n");
break;
}
fflush(stdout);
printf("%c", ch);
}
}
}

读取数据以回车符为分界,当读到回车符时进行换行处理,并等待接收下一波数据。

实验结果

Reference

Circular buffer


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!