使用共享内存和信号量:生产者和消费者问题

共享内存和信号量都是 Linux 下 IPC(Inter-Process Communication)的重要手段,尤其是共享内存,是最快的 IPC 方式也是最常用的方式之一。共享内存的基本思想是:同一块物理内存被映射到多个进程各自的虚拟内存空间,一个进程对共享内存数据的更新对其他进程是立即可见的,所以需要使用信号量(或者其他同步原语)来同步多个进程对共享内存的访问。图 1 显示了共享内存在进程虚拟地址空间中的位置:

图 1:共享内存

下面使用共享内存和信号量解决生产者和消费者问题,代码比较简单,相关 API 的使用方法可以参照 Linux Manual 或者 《Linux系统编程手册》这本书。

#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <stdio.h>

int main()
{
const int SIZE = 10;
int length = SIZE * sizeof(int);

int *buffer = NULL;
int head = 0, tail = 0;

sem_t *empty = sem_open("/empty", O_CREAT, S_IRUSR | S_IWUSR, SIZE);
sem_t *full = sem_open("/full", O_CREAT, S_IRUSR | S_IWUSR, 0);
sem_t *mutex = sem_open("/mutex", O_CREAT, S_IRUSR | S_IWUSR, 1);

int fd;
if ((fd = shm_open("/shm", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)) == -1) {
printf("shm_open failed!\n");
exit(1);
}

if (ftruncate(fd, length) == -1) {
printf("ftruncate failed!\n");
exit(1);
}

void *ptr = NULL;
if ((ptr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
printf("mmap failed!\n");
exit(1);
}
buffer = (int *)ptr;

if (fork()) {
for (int i = 0; i < SIZE; ++i) {
sem_wait(empty);
sem_wait(mutex);

sleep(1);
buffer[tail++] = i;
printf("producer: %d\n", i);
fflush(stdout);

sem_post(mutex);
sem_post(full);
}
} else {
for (int i = 0; i < SIZE; ++i) {
sem_wait(full);
sem_wait(mutex);

sleep(1);
printf("consumer: %d\n", buffer[head++]);
fflush(stdout);

sem_post(mutex);
sem_post(empty);
}
exit(0);
}

wait(NULL);

sem_unlink("/empty");
sem_unlink("/full");
sem_unlink("/mutex");
shm_unlink("/shm");

exit(0);
}
0%