C/C++ Linux 不同进程信号量及数据共享-生产者消费者问题
此为华南理工大学操作系统实验三,by 2020 Network Engineering VK,详情如下:
实验要求:
1.生产者消费者问题(信号量)
参考教材中的生产者消费者算法,创建5个进程,其中两个进程为生产者进程,3个进程为消费者进程。一个生产者进程试图不断地在一个缓冲中写入大写字母,另一个生产者进程试图不断地在缓冲中写入小写字母。3个消费者不断地从缓冲中读取一个字符并输出。为了使得程序的输出易于看到结果,仿照的实例程序,分别在生产者和消费者进程的合适的位置加入一些随机睡眠时间。
可选的实验:在上面实验的基础上实现部分消费者有选择地消费某些产品。例如一个消费者只消费小写字符,一个消费者只消费大写字母,而另一个消费者则无选择地消费任何产品。消费者要消费的产品没有时,消费者进程被阻塞。注意缓冲的管理。
实现思路:
信号量:
采用semaphore.h,头文件各函数详细说明可参考此页<semaphore.h> (opengroup.org),英文难看看不懂可看这个博客semaphore – 简书 (jianshu.com),注意点:init时注意要设置为进程间作用,而不是限制于同一进程内。
数据共享:
摸索许久,没有像多线程编程那样的直接变量共享方法,最后采用了共享内存,使用方法参考博客:Linux进程间通信(六):共享内存 shmget()、shmat()、shmdt()、shmctl() – 52php – 博客园 (cnblogs.com),如果不是深入了解的话,此博客直接看共享内存怎么用就行
程序执行:
将要读写共享内存时流程如下:
尝试获取信号量—>读/写共享内存—>归还信号量
源码:
希望大家可以从我的源码中获得收获/学习,而不是直接复制粘贴应付实验。
头文件
shmdata.h
#ifndef _SHMDATA_H_HEADER
#define _SHMDATA_H_HEADER
struct shared_use_st
{
int Acounter; // 大写计数
int acounter; // 小写计数
};
主程序:
#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <semaphore.h>
#include <sys/shm.h>
#include "shmdata.h"
using namespace std;
int main()
{
pid_t fpid;
int fatherpid = getpid();
sem_t sm;
sem_init(&sm, 1, 1);
void *shm = NULL;
struct shared_use_st *shared; // 指向shm
// 创建共享内存
int shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
if (shmid == -1)
{
fprintf(stderr, "创建共享内存失败\n");
exit(EXIT_FAILURE);
}
// 创建子进程
for (int i = 0; i < 5; ++i)
{
fpid = fork(); //返回子进程的进程ID
if (fpid == 0 || fpid < 0)
{
break;
}
}
// 将共享内存连接到当前进程的地址空间
shm = shmat(shmid, 0, 0);
if (shm == (void *)-1)
{
fprintf(stderr, "共享内存连接失败\n");
exit(EXIT_FAILURE);
}
printf("\n共享内存连接至 %X\n", (int*)shm);
// 设置共享内存
shared = (struct shared_use_st*)shm; // 注意:shm有点类似通过 malloc() 获取到的内存,所以这里需要做个 类型强制转换
// 根据子进程的身份不同,安排任务
if (fpid <= 0)
// 子进程处理
{
int diff = getpid() - fatherpid;
if (diff % 2 == 0)
// 生产者
{
if (diff == 2)
// 生产大写字母A
{
while(1)
{
sem_wait(&sm); //获取资源 ,相当于P操作
shared->Acounter = shared->Acounter + 1;
cout << "A生产者 A:\t" << shared->Acounter << "\ta:" << shared->acounter << endl;
sem_post(&sm); //释放资源,相当于V操作
sleep(1);
}
}else
// 生产小写字母a
{
while(1)
{
sem_wait(&sm); //获取资源 ,相当于P操作
shared->acounter = shared->acounter + 1;
cout << "a生产者 A:\t" << shared->Acounter << "\ta:" << shared->acounter << endl;
sem_post(&sm); //释放资源,相当于V操作
sleep(1);
}
}
}else
// 消费者
{
if (diff == 1)
// A消费者
{
while(1)
{
sem_wait(&sm); //获取资源 ,相当于P操作
if (shared->Acounter > 0)
{
shared->Acounter = shared->Acounter - 1;
cout << "A消费者 A:\t" << shared->Acounter << "\ta:" << shared->acounter << endl;
}
sem_post(&sm); //释放资源,相当于V操作
sleep(1);
}
}
else if (diff == 3)
// a消费者
{
while(1)
{
sem_wait(&sm); //获取资源 ,相当于P操作
if (shared->acounter > 0)
{
shared->acounter = shared->acounter - 1;
cout << "a消费者 A:\t" << shared->Acounter << "\ta:" << shared->acounter << endl;
}
sem_post(&sm); //释放资源,相当于V操作
sleep(1);
}
}
else if (diff == 5)
// aA消费者
{
while(1)
{
sem_wait(&sm); //获取资源 ,相当于P操作
if (shared->Acounter > 0)
{
shared->Acounter = shared->Acounter - 1;
cout << "aA消费者 A:\t" << shared->Acounter << "\ta:" << shared->acounter << endl;
}else if (shared->acounter > 0)
{
shared->acounter = shared->acounter - 1;
cout << "aA消费者 A:\t" << shared->Acounter << "\ta:" << shared->acounter << endl;
}
sem_post(&sm); //释放资源,相当于V操作
sleep(1);
}
}
}
}else
// 父进程处理
{
cout << "父进程" << getpid() << endl;
}
return 0;
}
运行截图: