std::shared_mutex跨进程完全不可用,必须使用sem_open(Linux)、CreateSemaphoreExW(Windows)或mmap+pthread_mutex_t(设PTHREAD_PROCESS_SHARED属性)等系统级原语。

std::shared_mutex 在跨进程场景下完全不可用
它只在单进程内有效,底层依赖线程 ID 和内存地址空间,跨进程后线程 ID 无意义,std::shared_mutex 的状态根本无法同步。强行用会导致死锁、假释放、甚至静默数据损坏——不是“不工作”,而是“看起来工作,实际崩溃在随机时间点”。
真正能用的只有操作系统级原语:sem_open(POSIX)、CreateSemaphoreExW(Windows)或基于 mmap + pthread_mutex_t 的命名互斥量(需显式设置 PTHREAD_PROCESS_SHARED 属性)。
- Linux 下优先选
sem_open:语义清晰、自动持久化、支持信号量计数,适合读写锁建模 - Windows 下避免
CreateMutexW:它不支持“共享内存内嵌”,必须配合MapViewOfFile手动管理生命周期 - 别碰
boost::interprocess::named_mutex:它封装了底层,但异常路径下容易残留信号量,调试时sem_unlink忘删会卡死后续启动
用 pthread_mutex_t + mmap 实现可读写共享锁的关键三步
这是最可控、移植性较好(glibc ≥ 2.12)的方式,核心在于把互斥量“放”进共享内存,并告诉内核它要跨进程使用。
常见错误是直接在 mmap 返回的地址上 pthread_mutex_init —— 缺少 PTHREAD_PROCESS_SHARED 属性,结果仍是进程私有。
立即学习“C++免费学习笔记(深入)”;
- 分配共享内存时用
shm_open+ftruncate,大小至少为sizeof(pthread_mutex_t)+ 缓冲区(如结构体) -
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED)必须调用,且不能漏掉pthread_mutexattr_init - 首次初始化者需检查
mutex是否已初始化(可用一个标志位 +__atomic_load_n判断),否则多进程竞争 init 会 UB
示例片段:
char *shm = (char*)mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); pthread_mutex_t *mtx = (pthread_mutex_t*)(shm + offsetof(my_shm_struct, lock)); pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); pthread_mutex_init(mtx, &attr); // 仅由第一个进程调用
sem_open 创建读写锁时的权限与清理陷阱
sem_open 的名字必须以 / 开头(POSIX 要求),且长度受限(Linux 通常 ≤ 255 字节,含前导斜杠)。更隐蔽的问题是:如果进程崩溃未调用 sem_close 或 sem_unlink,信号量会一直存在,下次 sem_open 会复用旧状态——导致锁永远无法获取。
- 开发期务必加
atexit([]{ sem_unlink("/myapp_rwlock"); }),但注意:仅对创建者生效,其他进程不能 unlink - 生产环境应设计“心跳+超时”机制,比如用共享内存中一个
uint64_t last_seen字段,配合定时器检测陈旧持有者 - 不要用
O_EXCL | O_CREAT反复尝试——失败后应先sem_unlink再重试,否则EEXIST会卡住 -
sem_wait可被信号中断,返回-1且errno == EINTR,必须循环重试,否则锁可能永远拿不到
Windows 上 CreateSemaphoreExW 的命名与安全描述符坑
Windows 不允许跨进程直接共享 HANDLE,所以必须用全局命名("Global\MyAppLock"),且注意 session 隔离:服务进程默认在 Session 0,GUI 进程在 Session 1,不加 Global\ 前缀就只能同 session 访问。
另一个高频问题是权限:默认安全描述符禁止非管理员进程打开,导致 OpenSemaphore 返回 NULL,GetLastError() 是 ERROR_ACCESS_DENIED。
- 创建时传入
SECURITY_ATTRIBUTES,其中lpSecurityDescriptor设为nullptr仅适用于测试;生产必须调用InitializeSecurityDescriptor+SetSecurityDescriptorDacl开放SEMAPHORE_ALL_ACCESS - 不要用
CreateSemaphore:它不支持dwMaximumCount > 1的读写锁建模,CreateSemaphoreExW才支持指定初始和最大计数 - 关闭 HANDLE 后,其他进程仍可继续用该命名信号量——Windows 不自动销毁,必须显式调用
CloseHandle并依赖最后一个句柄关闭后系统回收,但回收时机不确定
munmap、命名冲突——这些问题往往在压测或异常重启后才暴露,且难以复现。建议所有共享资源都绑定到一个统一的命名空间前缀(如 /myapp_v2_),升级时直接换前缀,比修残留干净得多。










