writer.cpp 如下:
#include <iostream>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#define SHM_KEY 0x1234
#define BUF_SIZE 1024
struct ShmRingBuffer
{
unsigned int write_index;
unsigned int read_index;
char buffer[BUF_SIZE];
};
int main()
{
//SHM_KEY: 共享内存标识 ; 0666:读写权限 ; IPC_CREAT:如果不存在就创建
int shmid = shmget(SHM_KEY, sizeof(ShmRingBuffer), 0666 | IPC_CREAT);
if (shmid < 0)
{
perror("shmget");
return -1;
}
//shmat作用:把内核里的共享内存,映射到当前进程的虚拟地址空间
//成功之后:shm 就是一个普通结构体指针
ShmRingBuffer* shm = (ShmRingBuffer*)shmat(shmid, NULL, 0);
// 初始化(第一次运行)
shm->write_index = 0;
shm->read_index = 0;
const char* msg = "Hello Shared Memory!";
int len = strlen(msg);
// 判断剩余空间
unsigned int free_space;
if (shm->write_index >= shm->read_index)
free_space = BUF_SIZE - (shm->write_index - shm->read_index);
else
free_space = shm->read_index - shm->write_index;
if (free_space <= len)
{
std::cout << "Buffer full!" << std::endl;
return -1;
}
// 写入(环形)
for (int i = 0; i < len; i++)
{
shm->buffer[shm->write_index] = msg[i];
//严格环形缓冲区通常要保留1字节,否则无法区分“满”和“空”。
shm->write_index = (shm->write_index + 1) % BUF_SIZE;
}
std::cout << "Write Done\n";
shmdt(shm);
return 0;
}
reader.cpp 如下:
#include <iostream>
#include <cstring>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#define SHM_KEY 0x1234
#define BUF_SIZE 1024
struct ShmRingBuffer
{
unsigned int write_index;
unsigned int read_index;
char buffer[BUF_SIZE];
};
int main()
{
int shmid = shmget(SHM_KEY, sizeof(ShmRingBuffer), 0666);
if (shmid < 0)
{
perror("shmget");
return -1;
}
//把内核中的共享内存映射到当前进程地址空间
ShmRingBuffer* shm = (ShmRingBuffer*)shmat(shmid, NULL, 0);
char data[128] = {0};
int i = 0;
while (shm->read_index != shm->write_index)
{
data[i++] = shm->buffer[shm->read_index];
shm->read_index = (shm->read_index + 1) % BUF_SIZE;
}
std::cout << "Read Data: " << data << std::endl;
shmdt(shm);
return 0;
}
环形队列(Ring Buffer)是共享内存通信中最经典、最高效的数据结构之一:
一、环形队列的核心思想
一句话概括:
用一块固定大小的连续内存,通过“读指针”和“写指针”循环移动,实现不断复用空间的队列结构。
核心元素只有两个:
read_index // 读位置
write_index // 写位置
以及一个固定数组:
buffer[N]
三、环形的真正价值:
1️⃣ 空间完全复用
普通数组:
用过的前半段浪费。
环形数组:
读过的空间立即可复用。
2️⃣ O(1) 时间复杂度
入队:
buffer[write] = data
write++
出队:
data = buffer[read]
read++
没有:
-
内存移动
-
realloc
-
malloc
性能极高
3️⃣ 非常适合生产者-消费者模型
共享内存典型模型:
生产者(写进程)
消费者(读进程)
只要保证:
读写不同索引:
就可以:
-
无锁(单生产者单消费者)
-
极低延迟
-
高吞吐
四、为什么共享内存必须用环形队列?
如果不用环形,你会遇到问题:
❌ 1. 需要移动内存
比如:
memmove(buffer, buffer + read_index, 剩余长度)
这样:
-
CPU 消耗大
-
多进程下风险高
-
性能极差
❌ 2. 需要动态扩展
共享内存大小是固定的:
shmget(size)
不能随便扩容。
环形结构刚好适配“固定大小”的限制。
❌ 3. 数据会丢失或浪费
如果不用环形:
-
写满后必须停止
-
或重新申请
这对实时系统是灾难。
它模拟的是:
流水线
六、核心数学模型
定义:
空:read == write
数据长度:
(write - read + size) % size
剩余空间:
(size - 数据长度 - 1)
这里的:
+ size
% size
是为了处理回绕。
七、为什么不用链表?
| 链表 | 环形数组 |
|---|---|
| 需要 malloc | 固定内存 |
| 不连续 | 连续内存 |
| cache不友好 | cache友好 |
| 多进程复杂 | 多进程简单 |
八、什么时候环形队列最强?
最典型场景:
-
串口通信
-
网络收发
-
日志缓冲
-
音视频流
-
嵌入式系统
-
多进程通信
九、核心思想总结
环形队列的核心思想是:
-
用固定空间
-
用两个指针
-
通过取模实现空间循环
-
避免内存移动
-
实现高效生产者-消费者通信
十、再给一句终极总结
普通队列是:
空间向前增长
环形队列是:
指针向前走,空间在循环
4289

被折叠的 条评论
为什么被折叠?



