mmap返回MAP_FAILED的常见原因包括:/dev/shm空间不足、大页内存权限未配置、文件描述符无O_RDWR权限或未调用ftruncate、64位映射与ASLR冲突、shm_open路径不合法或memfd_create不可用。

共享内存映射失败:mmap 返回 MAP_FAILED 的常见原因
多数人卡在第一步——连共享内存都映射不上,直接返回 MAP_FAILED。这不是代码写错了,而是环境或权限没对齐。
关键检查点:
-
/dev/shm空间不足(尤其传输 GB 级数据时),用df -h /dev/shm查看,临时扩容可执行sudo mount -o remount,size=4G /dev/shm - 普通用户默认无权创建大页共享内存,若用了
MAP_HUGETLB,需提前配置sysctl vm.nr_hugepages=128并加入/etc/security/limits.conf - 传给
mmap的文件描述符必须是打开后带O_RDWR权限的,且对应文件已用ftruncate设置好大小;漏掉ftruncate是高频错误 - 64 位程序在某些旧内核上对 >2GB 映射可能因 ASLR 冲突失败,可加
MAP_FIXED_NOREPLACE(慎用)或换用shm_open+mmap组合
shm_open vs memfd_create:选哪个更稳妥?
shm_open 兼容性好,但路径名全局可见、需手动清理、有命名冲突风险;memfd_create(Linux 3.17+)返回匿名 fd,自动生命周期管理,更适合大数据块。
实操建议:
- 跨进程稳定传输优先用
shm_open("/mydata", O_RDWR | O_CREAT, 0600),注意前缀必须带/,否则行为未定义 - 单次大块传输(如视频帧、模型权重加载)推荐
memfd_create("payload", MFD_CLOEXEC | MFD_ALLOW_SEALING),之后调用fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL)防止意外截断 - 不要混用:一个进程用
shm_open,另一个用memfd_create—— 它们底层不互通 - Windows 下无等价物,得切回
CreateFileMapping+MapViewOfFile,别硬套 Linux 接口
同步不是加个 std::atomic_flag 就完事:缓存一致性与内存序陷阱
共享内存里放一个 std::atomic_flag 当就绪标志?危险。x86 上看似能跑通,ARM 或带弱内存模型的 CPU 上大概率出错。
真正可靠的同步组合:
- 写端:写完数据 →
std::atomic_thread_fence(std::memory_order_release)→ 写标志为 true - 读端:轮询标志 →
std::atomic_thread_fence(std::memory_order_acquire)→ 读数据 - 标志变量本身必须是
std::atomic(非volatile bool),且声明为alignas(64)避免伪共享 - 若需多生产者/多消费者,别手写 ring buffer,直接用
boost::lockfree::spsc_queue(单生产单消费)或moodycamel::ConcurrentQueue(多线程安全),它们内部已处理内存序和 padding
避免隐式拷贝:零拷贝传输的关键边界条件
你以为用了共享内存就 100% 零拷贝?不一定。只要出现以下任一操作,数据就悄悄被复制了:
- 用
std::vector包裹共享内存地址 —— 析构时会调用delete[],而这块内存根本不是new出来的 - 把共享内存指针传给
std::string_view后,再传给某个函数,该函数内部调用了.data()+.size()做 memcpy —— 这不是你的错,是接口契约没看清 - 通过
std::shared_ptr管理共享内存生命周期 —— 引用计数本身要原子操作,且deleter必须精确匹配分配方式(munmaporclose),否则泄漏或 double-free - 正确姿势:用裸指针 + 显式作用域控制,或封装成 RAII 类(构造开/映射,析构关/解映),并在类内禁用拷贝,只允许移动
最常被忽略的一点:即使所有逻辑都正确,若共享内存段被 swap 到磁盘(缺页中断频繁),性能也会断崖下跌。务必在初始化后调用 mlock() 锁住物理页,但要注意 RLIMIT_MEMLOCK 限制。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











