c++怎么实现进程间通信共享内存_c++ Windows/Linux共享内存创建【详解】

15次阅读

windows用CreateFileMapping+MapViewOfFile、linux用shm_open+mmap实现共享内存,二者均需注意大小设置、错误处理、同步机制及跨平台兼容性,且共享内存中不可存放std::String等非平凡类型。

c++怎么实现进程间通信共享内存_c++ Windows/Linux共享内存创建【详解】

windows 下用 CreateFileMappingMapViewOfFile 创建共享内存

Windows 原生共享内存依赖内存映射文件(Memory-Mapped File),即使不关联磁盘文件,也能通过 INVALID_HANDLE_VALUE 创建纯内存段。关键不是“文件”,而是“映射对象”。

常见错误是传错 dwMaximumSizeHigh/dwMaximumSizeLow —— 它们合起来是 64 位大小,但多数场景用不到高位,直接设为 0 即可;若实际大小超 4GB,才需拆分赋值。

  • CreateFileMapping 返回 NULL 时,务必调用 GetLastError() 判断原因:权限不足(ERROR_access_DENIED)、名字冲突(ERROR_ALREADY_EXISTS)或内存不足(ERROR_NOT_ENOUGH_MEMORY)都得区分处理
  • 映射视图前必须确保 hMap 有效,且 MapViewOfFiledwNumberOfBytesToMap 不能超过创建时声明的大小
  • 多个进程访问同一块共享内存时,**没有内置同步机制**,必须额外配 CreateEventCreateMutex 控制读写顺序
HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, TEXT("MySharedMem")); if (hMap == NULL) {     // 处理错误 } LPVOID pMem = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 1024); if (pMem == NULL) {     CloseHandle(hMap); }

Linux 下用 shm_open + mmap 创建 POSIX 共享内存

Linux 推荐用 POSIX 标准接口shm_open 创建/打开一个共享内存对象(本质是 /dev/shm/ 下的虚拟文件),再用 mmap 映射进地址空间。它比 sysv shmshmget 系列)更现代、路径可控、权限清晰。

容易忽略的是 shm_openoflag 参数:跨进程通信必须带 O_RDWR,首次创建还要加 O_CREAT;同时 mode(如 0666)决定其他用户能否打开——若不设,可能被 umask 截断导致权限不足。

立即学习C++免费学习笔记(深入)”;

  • shm_open 返回的 fd 必须在 mmapclose(),否则资源泄漏(fd 不释放,但映射仍有效)
  • mmapLength 必须与 ftruncate 设置的大小一致,否则读写越界或 SEGV
  • 进程退出时不自动销毁共享内存,需显式调用 shm_unlink(仅删除名字,已映射的仍可用)
int fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0666); ftruncate(fd, 1024); void* ptr = mmap(nullptr, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); // fd 可关,ptr 仍有效

跨平台封装要注意的兼容性陷阱

Windows 和 Linux 的共享内存 API 差异大,强行抽象成统一接口容易踩坑。最现实的做法是按平台条件编译,或只封装“创建/映射/销毁”三步,把同步逻辑完全交给上层。

典型陷阱包括:

  • 名字格式:Windows 支持任意字符串(如 "MyMem"),Linux 的 shm_open 要求以 / 开头且不能含其它斜杠("/my_mem" 合法,"/a/b" 非法)
  • 大小限制:Windows 单个映射上限约 2GB(32 位进程),Linux 默认 /dev/shm 大小常为 64MB,可通过 mount -o remount,size=2G /dev/shm 调整
  • 生命周期管理:Linux 的 shm_unlink 类似于 Windows 的 CloseHandle + 名字注销,但 Windows 没有等价的“仅删名不关句柄”操作

为什么共享内存里不能放 std::string虚函数对象

共享内存本质是一段裸字节区域,所有数据必须是 trivially copyable,且地址无关。一旦结构体里包含 std::stringstd::vector指针、虚表指针或非 POD 成员,就绝不能直接 memcpy 进去。

例如:Struct Data { std::string name; int id; }; —— name 内部指针指向内存,其他进程无法访问;虚函数对象的 vptr 指向本进程的虚表,跨进程失效。

  • 正确做法是用固定长度 C 风格数组(char name[64])、手动序列化、或使用 boost::interprocess 提供的容器(如 boost::interprocess::basic_string
  • 若必须动态结构,建议只存偏移量+长度,把真实数据也布局在同一块共享内存中(即“内存池 + 相对指针”模式)
  • 所有字段必须显式对齐(alignas(8)),避免不同编译器填充差异导致读写错位

共享内存本身不难创建,难的是让两个独立进程安全、稳定、一致地解释同一片内存里的比特。类型布局、生命周期、同步粒度,这三样漏掉任何一环,都会在某个并发时机突然崩掉。

text=ZqhQzanResources