共享内存与同步机制专题

共享内存是最快的 IPC,但需要配合同步原语。

一、共享内存用法

1. POSIX mmap 临时文件(推荐)

int fd = open("/tmp/myshm", O_CREAT|O_RDWR, 0666);
ftruncate(fd, SIZE);
void *p = mmap(NULL, SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
// 关闭 fd 也不影响映射

2. POSIX 共享内存对象(更便携)

int fd = shm_open("/myregion", O_CREAT|O_RDWR, 0666);
ftruncate(fd, SIZE);
void *p = mmap(NULL, SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

3. POSIX 共享内存 /dev/shm

4. System V 共享内存(旧)

int shmid = shmget(IPC_PRIVATE, SIZE, IPC_CREAT | 0666);
void *p = shmat(shmid, NULL, 0);

5. memfd_create(Linux 特有)

int fd = memfd_create("mybuf", 0);
ftruncate(fd, SIZE);
void *p = mmap(NULL, SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

二、同步原语

1. POSIX 信号量(跨进程)

sem_t *sem = sem_open("/my_sem", O_CREAT, 0664, 1);
sem_wait(sem);
// 临界区
sem_post(sem);
sem_close(sem);
sem_unlink("/my_sem");

2. System V 信号量

3. futex(Linux 内核)

#include <linux/futex.h>
#include <sys/syscall.h>

int futex(int *uaddr, int op, int val,
          const struct timespec *timeout, int *uaddr2, int val3);

futex(&lock, FUTEX_WAIT, 0, NULL, NULL, 0);   // 等待 lock==0
futex(&lock, FUTEX_WAKE, 1, NULL, NULL, 0);   // 唤醒 1 个

三、无锁队列(Lock-free Queue)

1. 经典单生产者单消费者(SPSC)环形队列

struct spsc {
    char *buf;
    size_t cap;
    atomic_size_t head;   // 写
    atomic_size_t tail;   // 读
};
// 关键:写入完成后才发布 head;读取完成后才发布 tail

2. Michael-Scott 多生产者多消费者队列

四、内存屏障

// GCC 内建
__sync_synchronize();                                // 全屏障
__atomic_thread_fence(__ATOMIC_ACQUIRE);             // acquire
__atomic_thread_fence(__ATOMIC_RELEASE);             // release

规则:
- Acquire:紧随其后的读不会被重排到前
- Release:之前的写不会被重排到后
- Sequential Consistency:最强

五、性能对比

方式 延迟 吞吐
pipe(4KB) ~1us
Unix Domain Socket ~1us
TCP localhost ~10us
共享内存 + 信号量 < 100ns
共享内存无锁 < 50ns 极高

六、典型模式

1. 共享内存 + 信号量(推荐入门)

struct shared_data {
    pthread_mutex_t m;
    int             counter;
};

int shm_fd = shm_open("/my", O_CREAT|O_RDWR, 0666);
ftruncate(shm_fd, sizeof(struct shared_data));
void *p = mmap(NULL, sizeof(struct shared_data),
               PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0);
// p->m 必须是 PTHREAD_PROCESS_SHARED(详见下)

关键:要让 pthread mutex 跨进程,必须:

pthread_mutexattr_t a;
pthread_mutexattr_init(&a);
pthread_mutexattr_setpshared(&a, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&((struct shared_data*)p)->m, &a);

2. eventfd + 共享内存

七、调试