1270 字
6 分钟
多进程通讯下的生产者消费者问题
2026-06-18

概述#

在本文章中,我们将继续使用信号量机制来进行多线程中的生产者消费者问题。 核心思想与线程间的通讯完全一样,但是进程间是没有办法访问同一个程序下的资源的,我们需要对地址进行操作,因此我们可以得到本文较上一篇文章多了如下部分: 进程的创建,共享内存的申请,如何在内存中申请可供不同进程访问的信号量

修改原有信号量机制#

值得高兴的是修改原有信号量机制时,无需修改头文件库,下文中用到的sem_t,以及即将用到的sem_open均在semaphore.h库下。
将原有的信号量变量转化为信号量指针:
从:

sem_t mutex, full, empty

转化为:

sem_t* mutex;
sem_t* full;
sem_t* empty;

使用sem_open创建并初始化信号量,参考格式:
sem_t * sem_open(const char *name, int oflag, mode_t mode, unsigned int value); name: 信号量名称必须以 / 开头,且后面不能出现任何其余 /
oflag: 操作标志,提供参数:O_CREATO_CREAT | O_EXCL (在fcntl.h库下),前者代表没有就进行创建,后者代表若已经存在,则退出程序,并报错,确保在系统中只有一个独特的信号量。O_EXIT mode: 权限掩码,提供一个4位int类型数字,例如0600;

第一位:特殊权限, 0 代表无特殊权限;
第二位:文件所有者;
第三位:同组用户;
第四位:其它用户;
核心权限: 1 执行权限,2 写权限, 4 读权限。

value: 初始值的大小;

进行初始化#

'''cpp mutex = sem_open(“/sem_mutex”, O_CREAT, 0600, 1); full = sem_open(“/sem_full”, O_CREAT, 0600, 0); empty = sem_open(“/sem_empty”, O_CREAT, 0600, N); '''

申请公共内存#

具体操作:

  1. 申请标识符,通过调用sys/mman.h库下的shm_open进行初始化。
    参考格式: int name = shm_open(const char *name, int oflag, mode_t mode),
    name: 共享内存在本地目录下存储的名称,必须以 / 开头,且不能有任何其余 /
    oflag: 打开方式,O_CREAT:不存在则创建;O_RDWR:可读写;O_RDONLY:只读;O_EXCL:与O_CREAT同时使用时,如果检测到同名地址空间,则退出程序。
    mode: 权限掩码。
  2. 分配地址空间,通过调用unistd.h库下的ftruncate进行空间的申请。
    参考格式: fturncate(int fd, off_t length),
    fd: 打开的文件描述符。
    length: 待申请的内存地址空间。
  3. 内存地址的映射,通过调用sys/mman.h库下的mmap等函数来进行映射操作。 参考格式: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); **addr**: 设定起始内存地址,通常设置为NULL,让内核自动分配。 **length**: 映射地址长度。 **prot**: 映射区域的权限,在sys/mman.h库下提供四种权限: **PROT_NONE**不可访问; **PROT_READ**可读; **PROT_WRITE**可写; **PORT_EXEC**可执行。 **flags**: 控制映射类型,在sys/mman.h`库下提供三种类型: MAP_SHARED:所有映射该文件的进程共享内存空间; MAP_PRIVATE:私有映射,是MAP_SHARED的逆操作;MAP_ANONYMOUS:匿名共享,用于为进程间共享的大块堆内存。
    fd: 文件描述符。
    offset: 文件偏移量,通常设置为0

参考代码:

#include <sys/mman.h>
#include <unistd.h>
int main(){
int fd = shm_open("/shm_data", O_CREAT | O_RDWR, 0600);
ftruncate(fd, sizeof(ShareData));
ShareData* data = (ShareData*)mmap(NULL, sieof(ShareData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
data -> item = 0; // 并对映射的内存空间中的元素进行初始化
}

创建进程#

使用unistd.h库下的fork()函数创建进程, 并利用PID来判断进程执行的代码段:

pid_t pid_productor = fork();
if(pid_productor == 0){
// 生产者对应的代码段
exit(0);
}else{
pid_t pid_consumor = fork();
if(pid_consumor == 0){
// 消费者对应的代码段
exit(0)
}else{
// 回收进程资源
}
}

系统资源的回收#

在父进程结束后,两个子进程并不会跟随结束,而是会继续进行,持续运行会造成系统资源的浪费,同时再次执行时,会导致原有公有内存和信号量的报错,因此我们需要进行系统资源的回收。

  1. 进程的回收:
/*
在本文中我们创建了两个进程,因此需要等待两个进程的结束
*/
wait(NULL);
wait(NULL);
  1. 申请空间的回收:
    使用semaphore.h库下的sem_unlink()函数回收申请的信号量。
sem_unlink("/sem_mutex");
sem_unlink("/sem_full");
sem_unlink("/sem_empty");

使用sys/mman.h库下的shm_unlink()函数回收申请的公共内存。

shm_unlink("/sem_data");

至此我们说完了所有有关的信息,下面是一个完整的演示示例。

#include <iostream>
#include <unistd.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/wait.h>
const int N = 5;
struct ShareData{
int item;
};
sem_t *mutex;
sem_t *full;
sem_t *empty;
void productor(ShareData *data, sem_t *mutex, sem_t *full, sem_t *empty){
while(true){
sem_wait(empty);
sem_wait(mutex);
std::cout << data -> item++ << std::endl;
sem_post(mutex);
sem_post(full);
}
}
void consumor(ShareData *data, sem_t *mutex, sem_t *full, sem_t *empty){
while(true){
sem_wait(full);
sem_wait(mutex);
std::cout << data -> item-- << std::endl;
sem_post(mutex);
sem_post(empty);
}
}
int main(){
mutex = sem_open("/sem_mutex", O_CREAT, 0600, 1);
full = sem_open("/sem_full", O_CREAT, 0600, 0);
empty = sem_open("/sem_empty", O_CREAT, 0600, N);
int fd = shm_open("/shm_data", O_CREAT | O_RDWR, 0600);
ftruncate(fd, sizeof(ShareData));
ShareData* data = (ShareData*)mmap(NULL, sizeof(ShareData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
data -> item = 0;
pid_t pid_productor = fork();
if(pid_productor == 0){
productor(data, mutex, full, empty);
exit(0);
}else{
pid_t pid_consumor = fork();
if(pid_consumor == 0){
consumor(data, mutex, full, empty);
exit(0)
}else{
wait(NULL);
wait(NULL);
}
}
sem_unlink("/sem_mutex");
sem_unlink("/sem_full");
sem_unlink("/sem_empty");
shm_unlink("/sem_data");
}
多进程通讯下的生产者消费者问题
https://kuchikirei.github.io/posts/os3/
作者
LINKONG
发布于
2026-06-18
许可协议
CC BY-NC-SA 4.0