本页目录
共享内存与信号量
覆盖: shmget/shmat (SysV) → mmap(MAP_SHARED) → POSIX semaphore → file locking → memfd_create → 对比与选择 适用: Linux 用户态
概述
管道和 socket 传递数据靠拷贝,共享内存则让多个进程访问同一块物理内存——零拷贝。这是最高性能的 IPC 方式,但需要额外的同步机制(信号量、file lock、或 lockless 数据结构)。
mmap(MAP_SHARED): POSIX 共享内存
// 写法 1: 匿名共享内存 (fork 后共享)
void *addr = ;
// fork 后: 父子进程看到同一个物理页 (不是 COW!)
// 写法 2: 文件背景的共享内存
int fd = ;
;
void *addr = ;
// 不相关进程: open 同一文件 → mmap → 共享
// 各进程看到的同一物理页 → 改了就互相可见 → 需要同步!
memfd_create: 匿名文件句柄
int fd = ;
;
void *addr = ;
// 优势:
// - 无文件系统挂载 → 不需要 /dev/shm
// - 可以 sealing (防止修改): fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW)
// - 可以传给另一个进程: sendmsg(SCM_RIGHTS) → fd passing
// systemd 用 memfd 做所有内部 IPC
SysV 共享内存 (旧, 不推荐)
int shmid = ;
void *addr = ;
// ... read/write ...
;
;
// 为什么过时: 全局 key 冲突, 资源泄露风险, 不够灵活
POSIX 信号量
// 匿名信号量 (线程或共享内存):
sem_t sem;
; // pshared=1 → 在共享内存中
; // P (减 1, 阻塞)
; // V (加 1, 可能唤醒等待者)
;
// 命名信号量 (不相关进程):
sem_t *sem = ;
;
;
;
;
sem_wait 内部: futex
// glibc sem_wait 实现 (简化):
// 与 pthread_mutex 相同: 无竞争时零 syscall → futex 的力量
File Locking: 进程间互斥
// fcntl record lock: 对文件某区域加锁
struct flock fl = ;
; // 阻塞等待
// 优势: 内核自动管理 → 进程崩溃 → 锁自动释放
// 适合: 守护进程单实例, 数据库文件保护
选择指南
场景 推荐方案
─────────────────────────────────────────────
父子进程单向数据 pipe
父子进程双向数据 socketpair
无关联进程大块数据共享 mmap(MAP_SHARED) + POSIX sem
守护进程单实例 + 进程间锁 file lock (F_SETLK)
线程同步 (同进程) pthread_mutex (futex)
线程/进程间计数信号量 POSIX sem (futex)
可靠凭证传递 AF_UNIX + SCM_CREDENTIALS
参考
- man: mmap(2), shm_overview(7), sem_overview(7), fcntl(2) (record locks), memfd_create(2)
- 源码:
ipc/sem.c(SysV),ipc/shm.c,mm/mmap.c - LWN: "memfd_create and sealing"
关键词: mmap MAP_SHARED, memfd_create, POSIX semaphore, sem_wait, futex, file lock, F_SETLK, shared memory