Linux进程间用mmap共享内存需同步吗?原子操作可行吗?
问题背景
我有两个Linux进程,其中一个需要向另一个发送“信号”。用signal()实现难度较高,因为要存储服务端进程的PID等信息,所以打算用shm_open+mmap实现共享内存,方便未来拓展更多通信场景。现在有两个问题:
- 假设共享内存中存在一个bool变量,如何实现服务端与客户端的同步?
- 原子操作在跨进程场景下是否可行?若可行,工作原理是什么?
示例代码
客户端代码
// 共享内存中的bool变量b b = true;
服务端代码
// 共享内存中的bool变量b if (b){ refresh(); b = false; }
问题解答
一、共享内存中bool变量的同步实现
直接使用普通bool变量会存在竞态条件问题:比如客户端设置b=true的同时,服务端正在读取并重置b=false,可能导致信号丢失或重复处理。可以通过以下几种方式实现可靠同步:
用原子类型替代普通bool
把普通bool换成C11标准的_Atomic bool类型,或者Linux提供的atomic_t(针对整数,可模拟bool)。原子类型的读写操作都是原子性的,能避免竞态:- 客户端:
atomic_store(&b, true); - 服务端:
if (atomic_exchange(&b, false)) { refresh(); }atomic_exchange会原子性地读取当前值并设置为false,确保一次信号只会被处理一次。
- 客户端:
配合互斥锁(mutex)使用
在共享内存中放置一个pthread_mutex_t,初始化时设置为进程共享属性(PTHREAD_MUTEX_SHARED)。客户端修改b前加锁,服务端读取修改前也加锁:
客户端代码:pthread_mutex_lock(&mutex); b = true; pthread_mutex_unlock(&mutex);服务端代码:
pthread_mutex_lock(&mutex); if (b) { refresh(); b = false; } pthread_mutex_unlock(&mutex);这种方式适合更复杂的共享数据场景,但开销比原子操作略高。
使用信号量(semaphore)配合
用sem_open创建跨进程信号量,客户端设置bool后发信号,服务端等待信号量:
客户端代码:b = true; sem_post(sem);服务端代码:
sem_wait(sem); // 此时b一定为true refresh(); b = false;这种方式能让服务端主动等待,避免轮询消耗CPU,适合需要实时响应的场景。
二、原子操作在跨进程场景的可行性与原理
原子操作在跨进程场景下完全可行,核心原理如下:
- 硬件层面原生支持:现代CPU提供了原子指令(比如x86的
LOCK前缀指令、ARM的LDREX/STREX指令),这些指令能保证在多核心、多进程环境下,对内存地址的读写操作是不可分割的,不会被其他CPU核心的操作打断。 - 用户态无内核介入:Linux内核和C标准库(C11的
<stdatomic.h>)把硬件原子指令封装成了易用的原子操作接口,这些操作是用户态实现,不需要陷入内核调度,开销极低。 - 共享内存的物理一致性:共享内存通过
mmap映射到多个进程的虚拟地址空间,但最终指向同一块物理内存页。原子操作针对的是物理内存地址的原子性访问,和进程所属的虚拟地址空间无关,因此跨进程同样生效。
需要注意:原子操作仅保证单个操作的原子性,如果是多个操作的组合逻辑,还是需要额外同步机制,或者使用复合原子操作(比如atomic_compare_exchange_strong)。
内容的提问来源于stack exchange,提问作者Nick

