共享内存的问题在于如果发送进程频繁写数据,而读取进程没来得及读,会导致共享内存里的数据被覆盖,共享内存(shm_open + mmap)只是提供了一块所有进程都能访问的公共地址空间,它本身不带任何缓存、消息队列、版本控制的机制。操作系统不会帮你保存"历史数据",也不会阻止另一个进程覆盖你写的内容。
这意味着:
-
多个写者同时写 → 数据竞争(race condition);
-
写者频繁写而读者没跟上 → 前面写的内容会被新的覆盖;
-
除非你自己加同步机制 或缓冲结构,否则共享内存就只是"一块裸内存"。
使用 环形缓冲区(ring buffer)
原理是:
- 分配一块较大的共享内存;
- 定义头尾指针(
head,tail); - 写者往尾部写数据;
- 读者从头部读数据;
- 用原子操作或信号量控制 head/tail
这种方式可以:
✅ 支持多个连续消息;
✅ 避免频繁 mmap;
✅ 控制覆盖策略(例如"满了丢最旧"或"阻塞等待")。
c++
//shared_memory.h
#include <iostream>
#include <cstring>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <semaphore.h>
#include <sys/types.h>
#include <vector>
#include <atomic>
typedef struct img_data{
size_t lengh;
std::vector<char> data;
}IMG_DATA;
class SharedMemory{
public:
struct shared_header{
std::atomic<size_t> head_;
std::atomic<size_t> tail_;
size_t buffer_size;
unsigned char memory_[1];
};
SharedMemory(const std::string &name, size_t size, bool create=true);
~SharedMemory();
//get shared memory address
void * get_memory() const;
//notify other process data is ready.
void notify();
void wait();
int wait_until_timedout(int milliseconds);
size_t get_size() const;
bool write(const void *data, size_t len);
void write_data(size_t tail, const void * data, size_t len);
ssize_t read(void * out, size_t maxlen);
private:
std::string name_;
size_t size_;
shared_header * header_;
int shm_fd_;
sem_t *semaphore_;
std::string sem_name_;
bool creator_;
private:
void create_shared_memory(size_t size);
void attach_shared_memory();
};
源文件实现
c++
#include <iostream>
#include <cstring>
#include <sys/ipc.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <semaphore.h>
#include <sys/types.h>
#include <vector>
#include <atomic>
typedef struct img_data{
size_t lengh;
std::vector<char> data;
}IMG_DATA;
class SharedMemory{
public:
struct shared_header{
std::atomic<size_t> head_;
std::atomic<size_t> tail_;
size_t buffer_size;
unsigned char memory_[1];
};
SharedMemory(const std::string &name, size_t size, bool create=true);
~SharedMemory();
//get shared memory address
void * get_memory() const;
//notify other process data is ready.
void notify();
void wait();
int wait_until_timedout(int milliseconds);
size_t get_size() const;
bool write(const void *data, size_t len);
void write_data(size_t tail, const void * data, size_t len);
ssize_t read(void * out, size_t maxlen);
private:
std::string name_;
size_t size_;
shared_header * header_;
int shm_fd_;
sem_t *semaphore_;
std::string sem_name_;
bool creator_;
private:
void create_shared_memory(size_t size);
void attach_shared_memory();
};
uos@uos-PC:/software/code/test/tls_server/ipc_demo$
uos@uos-PC:/software/code/test/tls_server/ipc_demo$
uos@uos-PC:/software/code/test/tls_server/ipc_demo$ cat shared_memory.cpp
#include "shared_memory.h"
SharedMemory::SharedMemory(const std::string& name, size_t size, bool create)
: name_(name), size_(sizeof(shared_header) + size - 1), header_(nullptr), shm_fd_(-1), semaphore_(nullptr) {
creator_ = create;
// 创建或获取共享内存
if (create) {
create_shared_memory(size);
} else {
attach_shared_memory();
}
header_ = (shared_header*)mmap(nullptr, size_, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd_, 0);
if(header_ == MAP_FAILED){
throw std::runtime_error(std::string("mmap failed: ")+strerror(errno));
}
if(creator_){
header_->head_.store(0);
header_->tail_.store(0);
header_->buffer_size = size;
}
}
void SharedMemory::create_shared_memory(size_t size) {
std::string shm_name = "/" + name_;
sem_name_ = "/sem_"+name_;
shm_unlink(shm_name.c_str());
shm_fd_ = shm_open(shm_name.c_str(), O_CREAT|O_RDWR, 0666);
if(shm_fd_ == -1){
std::cerr << "shm_open failed: " << strerror(errno) << std::endl;
throw std::runtime_error("shm_open failed");
}
if(ftruncate(shm_fd_,size) == -1){
throw std::runtime_error("ftruncate failed");
}
sem_unlink(sem_name_.c_str());
// 创建信号量用于同步
semaphore_ = sem_open(sem_name_.c_str(), O_CREAT, 0644, 0);
if (semaphore_ == SEM_FAILED) {
std::cerr << "sem_open failed: " << strerror(errno) << std::endl;
throw std::runtime_error("sem_open failed");
}
}
void SharedMemory::attach_shared_memory() {
std::string shm_name = "/" + name_;
shm_fd_ = shm_open(shm_name.c_str(), O_RDWR, 666);
if(shm_fd_ == -1){
std::cerr << "shm_open failed: " << strerror(errno) << std::endl;
throw std::runtime_error("shm_open failed");
}
// 获取信号量
std::string semName = "/sem_" + name_;
semaphore_ = sem_open(semName.c_str(), 0);
if (semaphore_ == SEM_FAILED) {
std::cerr << "sem_open failed: " << strerror(errno) << std::endl;
throw std::runtime_error("sem_open failed");
}
}
SharedMemory::~SharedMemory() {
if (header_ != nullptr && header_ != (void*)-1) {
munmap(header_, size_);
}
if(shm_fd_!=-1){
close(shm_fd_);
}
if (semaphore_ != nullptr && semaphore_ != SEM_FAILED) {
sem_close(semaphore_);
}
if(creator_){
std::string shm_name = "/" + name_;
shm_unlink(shm_name.c_str());
sem_unlink(sem_name_.c_str());
}
}
void* SharedMemory::get_memory() const {
return header_->memory_;
}
void SharedMemory::notify() {
sem_post(semaphore_);
}
int SharedMemory::wait_until_timedout(int milliseconds) {
int return_status = 0;
struct timespec ts;
ts.tv_sec = milliseconds / 1000; //s
ts.tv_nsec = (milliseconds %1000) * 1000000; //ns
int ret = sem_timedwait(semaphore_, &ts);
if(ret!=0){
return_status = errno;
}
return return_status;
}
void SharedMemory::wait(){
sem_wait(semaphore_);
}
size_t SharedMemory::get_size() const {
return header_->buffer_size;
}
bool SharedMemory::write(const void *data, size_t len){
if(len + sizeof(size_t) > header_->buffer_size){
std::cerr<<"message too large for buffer\n";
return false;
}
while(true){
size_t head = header_->head_.load(std::memory_order_acquire);
size_t tail = header_->tail_.load(std::memory_order_acquire);
size_t used = (tail >= head)?(tail - head):(header_->buffer_size - head + tail);
size_t free_space = header_->buffer_size -used - 1; //reserve 1byte for empty or full flag
if(free_space < len + sizeof(size_t)){
//mem cache is full, waitting
usleep(1000);
continue;
}
write_data(tail, data, len);
return true;
}
return true;
}
void SharedMemory::write_data(size_t tail, const void * data, size_t len){
size_t data_len = static_cast<size_t>(len);
//write len
if(tail + sizeof(size_t) <= header_->buffer_size){
memcpy((unsigned char*)header_->memory_ + tail, &data_len, sizeof(size_t));
}
else{
size_t first = header_->buffer_size - tail;
memcpy((unsigned char*)header_->memory_+tail, &data_len, first);
memcpy((unsigned char*)header_->memory_, &data_len + first, sizeof(size_t) - first);
}
tail = (tail + sizeof(size_t)) % header_->buffer_size;
//write data
if(tail + len <= header_->buffer_size){
memcpy((unsigned char*)header_->memory_ + tail, data, len);
}
else{
size_t first = header_->buffer_size - tail;
memcpy((unsigned char*)header_->memory_+tail, data, first);
memcpy((unsigned char*)header_->memory_, (unsigned char*)data + first, len - first);
}
tail = (tail + len) % header_->buffer_size;
header_->tail_.store(tail, std::memory_order_release);
notify();
}
ssize_t SharedMemory::read(void * out, size_t maxlen){
size_t head = header_->head_.load(std::memory_order_acquire);
size_t tail = header_->tail_.load(std::memory_order_acquire);
if(tail == head){
return 0;
}
uint32_t data_len;
memcpy(&data_len, (unsigned char*)header_->memory_+head, sizeof(size_t));
head = (head + sizeof(size_t)) % header_->buffer_size;
if(data_len > maxlen) return -1;
if(head + data_len <= header_->buffer_size){
memcpy(out, (unsigned char*)header_->memory_+head, data_len);
}
else{
size_t first = header_->buffer_size - head;
memcpy(out, (unsigned char*)header_->memory_+head,first);
memcpy((unsigned char*)out+first, header_->memory_, data_len - first);
}
header_->head_.store((head + data_len)%header_->buffer_size, std::memory_order_release);
return data_len;
}
查看系统支持的消息队列最大长度,以及每条消息的长度
shell
uos@uos-PC:~$ cat /proc/sys/fs/mqueue/msg_max
10
uos@uos-PC:~$ cat /proc/sys/fs/mqueue/msgsize_max
8192
查看共享内存内容
shell
hexdump -C /dev/shm/my_shm | less
00000000 4e 00 00 00 00 00 00 00 4e 00 00 00 00 00 00 00 |N.......N.......|
00000010 00 00 a0 00 00 00 00 00 12 00 00 00 00 00 00 00 |................|
00000020 74 68 69 73 20 69 73 20 61 20 70 69 63 74 75 72 |this is a pictur|
00000030 65 2e 12 00 00 00 00 00 00 00 74 68 69 73 20 69 |e.........this i|
00000040 73 20 61 20 70 69 63 74 75 72 65 2e 12 00 00 00 |s a picture.....|
00000050 00 00 00 00 74 68 69 73 20 69 73 20 61 20 70 69 |....this is a pi|
00000060 63 74 75 72 65 2e 00 00 00 00 00 00 00 00 00 00 |cture...........|
00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
对应的数据结构
c
struct shared_header{
std::atomic<size_t> head_;
std::atomic<size_t> tail_;
size_t buffer_size;
unsigned char memory_[1];
};
可以看到head_为4e 00 00 00 00 00 00 00,tail_为4e 00 00 00 00 00 00 00,buffer_size为00 00 a0 00 00 00 00 00,每次存数据格式为长度+值,12 00 00 00 00 00 00 00为长度,74 68 69 73 20 69 73 20 61 20 70 69 63 74 75 72 65 2e 为值。tail和head相等表示数据已读完。