共享内存
共享内存
共享内存
- 共享内存是 Linux / Unix 系统提供的一种 进程间通信(IPC, Inter-Process Communication)机制:
- 允许 两个或多个进程访问同一块物理内存。
- 数据可以直接从内存中读写,无需复制,所以速度极快。
- 必须自己管理同步,否则会出现数据冲突或破坏。
Producer / Consumer 模式
- Producer: 负责生产数据(Camera / RGA 输出)
- Consumer: 负责消费数据(AI / 推理 / 模型)
- 核心原则:
- Producer 不阻塞
- Consumer 可以慢
- buffer 生命周期清晰
| 特性 | 说明 |
|---|---|
| 零拷贝 | Producer 写入内存,Consumer 直接读取,不经过 socket / 文件拷贝 |
| 低延迟 | CPU 不用做序列化 / 反序列化操作 |
| 跨进程 | 只要进程映射了同一块共享内存,任何进程都能访问 |
| 必须同步 | 没有内置锁,需要 mutex / semaphore / condition variable 来保证安全 |
Linux 下实现方式
- POSIX 共享内存
shm_open创建/打开ftruncate设置大小mmap映射到用户空间
- System V 共享内存(老式,不常用)
- DMA-BUF / GPU buffer(现代图像应用)
- 可以直接传递硬件 buffer fd 给其他进程
内存布局概念
共享内存不仅仅是一个 裸数据块,通常还包含:
- 数据区
- 图像 buffer、frame_id、timestamp
- 状态标志
- Producer / Consumer 状态,是否可读/可写
- 同步机制
- Mutex + Condition Variable
这三部分一起组成了共享内存结构体
Producer / Consumer 架构的概念
Producer / Consumer 是一种经典的并发模式:
Producer 写共享内存
Consumer 读共享内存
同步逻辑保证:
- Producer 不覆盖未消费的数据
- Consumer 不读无效数据
- CPU 不空转,条件变量阻塞
核心同步逻辑
1 | Producer Consumer |
头文件
| 头文件 | 用途 |
|---|---|
sys/mman.h |
mmap/munmap 映射共享内存 |
sys/stat.h |
文件权限模式常量(0666) |
fcntl.h |
shm_open, O_CREAT / O_RDWR |
unistd.h |
ftruncate, close |
pthread.h |
mutex / cond 等同步机制 |
cstring |
字符串操作,例如 strncpy |
共享内存结构体
1 | struct SharedBlock { |
函数详解
1 | int fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666); |
1 | shm_open |
- 打开或创建一个共享内存对象
- 参数:
SHM_NAME:共享内存名字O_CREAT | O_RDWR:创建 + 可读写0666:权限
1 | ftruncate(fd, SHM_SIZE) |
- 调整共享内存大小(必须指定 size = sizeof(SharedBlock))
1 | auto* shm = (SharedBlock*)mmap(nullptr, SHM_SIZE, |
mmap 映射共享内存到进程地址空间
参数:
PROT_READ | PROT_WRITE→ 可读写MAP_SHARED→ 对其他进程可见fd→ shm_open 的文件描述符
返回值:指向映射的内存,可以像普通指针一样操作
初始化互斥锁和条件变量
1 | pthread_mutexattr_t mattr; |
必须由一个进程初始化,其他进程只映射即可
生产数据逻辑
1 | pthread_mutex_lock(&shm->mutex); // 上锁 |
上锁:保证写入互斥
while检查状态:- 如果上一次数据还没被 Consumer 消费,Producer 阻塞
pthread_cond_wait:
写入数据:
1 | shm->frame_id++; |
- 先写数据,再标记
data_ready = true - 顺序关键:否则 Consumer 可能读到半写入的数据
通知 Consumer 并解锁:
1 | pthread_cond_signal(&shm->cond); // 唤醒 Consumer |
- signal → 唤醒 Consumer
- unlock → 允许 Consumer 获取 mutex
消费者逻辑
打开共享内存:
1 | int fd = shm_open(SHM_NAME, O_RDWR, 0666); |
- 不需要 O_CREAT
- Consumer 不初始化 mutex / cond,直接使用映射的
读取数据逻辑:
1 | pthread_mutex_lock(&shm->mutex); // 上锁 |
- 如果没有数据,阻塞等待
- 条件变量避免 busy loop → CPU 零占用
读取并消费:
1 | std::cout << shm->frame_id << " " << shm->payload << "\n"; |
- 先读,再清
data_ready - signal → 告诉 Producer 可以写下一帧
- unlock → 释放共享内存访问权
智能指针管理逻辑
共享内存里的指针操作用 智能指针 封装,方便管理,避免手动 mmap/munmap、shm_open/close 出错,同时保持线程安全。
智能指针管理共享内存的主要目标:
自动释放资源
依旧保持 Producer/Consumer 同步逻辑
mutex和cond仍然是原生pthread,智能指针不干预
安全、异常友好
- 如果程序异常退出,智能指针自动释放映射
智能指针
智能指针是一种 C++ 提供的 RAII 资源管理机制,它能自动管理动态分配的资源,防止内存泄漏和资源未释放。
std::unique_ptr<T> |
独占所有权 | std::unique_ptr<int> p(new int(5)); |
生命周期结束自动释放;不能拷贝,可移动 |
|---|---|---|---|
std::shared_ptr<T> |
共享所有权 | auto sp = std::make_shared<int>(5); |
多个指针共享同一资源,最后一个析构时释放 |
std::weak_ptr<T> |
弱引用 | std::weak_ptr<int> wp = sp; |
不增加引用计数,用于避免循环引用 |
为什么共享内存适合用智能指针
共享内存操作流程:
shm_open→ 获取 fdftruncate→ 设置大小mmap→ 映射munmap→ 释放映射close→ 关闭 fd(可选)
问题:
- 如果手动操作,一旦异常退出,
munmap可能没调用 → 内存泄漏 - fd 没关闭 → 资源泄漏
解决方案:
- 用
unique_ptr+ 自定义删除器封装mmap的地址 - 这样映射就能和智能指针绑定,出作用域自动释放
示例代码:
1 | // 自定义删除器 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 涵风 Blog!

