操作系统算法之生产者消费者算法模拟
实验目的
本实验通过参考和调试生产者-消费者模拟程序,进一步认识进程并发执行的实质,加深对进程竞争关系,协作关系的理解,掌握使用信号量机制与P、V操作来实现进程的同步与互斥。进而完成本实验的要求。
实验要求
参考生产者-消费者模拟程序,完成下题要求:
桌子上有一空盘,允许存放一只水果。爸爸可向盘中放苹果,也可向盘中放桔子,儿子专等吃盘中的桔子,女儿专等吃盘中的苹果。规定当盘空时一次只能放一只水果供吃者取用。
(1)使用P、V操作实现爸爸、儿子、女儿三个并发进程的同步的算法。
(2)用高级语言编写一个程序,爸爸、儿子、女儿进程并发执行过程,并采用信号量机制与P、V操作实现进程间同步。
(3)程序不能无休止运行下去,可自行设定停止条件,如爸爸往盘中放入四个水果后结束。
每个进程由一个进程控制块(PCB)表示,进程控制块结构可参考如下:
typedef struct Process//进程PCB
{
char name[10]; //进程名
int roleFlag; //进程类型(1:生产者 0: 消费者)
int currentState; //进程状态(1: 可运行态 0: 阻塞态)
int currentStep; //断点
char fruit[20] //水果名称
}Process;
实验分析
从题目要求来看,显而易见,生产者为父亲,消费者为儿子和女儿。桌子上有一空盘,允许存放一只水果。爸爸可向盘中放苹果,也可向盘中放桔子,儿子专等吃盘中的桔子,女儿专等吃盘中的苹果。规定当盘空时一次只能放一只水果供吃者取用。首先,根据题意可知,此题中包含三个进程,分别是父亲进程、儿子进程和女儿进程。其次,要根据每个关注的对象,来确定信号量。父亲进程关注的对象是盘子是否为空;儿子进程关注的对象是盘子里是否有橘子;女儿则是关注盘子里是否有苹果。分析知,只有当爸爸在盘子里放入了水果之后,儿子女儿才能取水果吃。故存在儿子和女儿进程等待爸爸进程的现象,即存在进程相互等待的现象,所以涉及进程同步问题。
PV原语表示
void Father()//父亲进程原语
{
while(true)
{
P(empty);
P(mutex);
put_in();//放入橘子或者苹果
V(mutex);
if(放入苹果)
{
V(apple);
}
else
{
V(orange);
}
}
}
//儿子女儿进程原语表示
void Son()
{
while(true)
{
P(orange);
P(mutex);
orange_out();//儿子拿走橘子
V(mutex);
V(empty);
}
}
void Daughter()
{
while(true)
{
P(apple);
P(mutex);
apple_out();//女儿拿走苹果
V(mutex);
V(empty);
}
}
整体流程图
父亲进程流程图
儿子女儿进程流程图
算法代码
#include "stdio.h"
#include "time.h"
#include "stdlib.h"
#include "string.h"
#include "windows.h"
#define processNum 3 //进程个数
typedef struct Process //进程PCB
{
char name[10]; //进程名
int roleFlag; //进程类型(1:生产者(父亲); 0:消费者(儿子女儿)
int currentState; //进程状态(1: 可运行态 0: 阻塞态)
int currentStep; //断点
char fruit[20]; //水果名称
int code; //进程编号
}Process;
typedef struct semaphore //信号量
{
int value; //信号量的值
int *pcq; //信号量队列指针
}semaphore;
int producerCongestionQueue[processNum]; //等待信号量empty的阻塞队列
int consumerOrangeCongestionQueue[processNum]; //等待信号量orange的阻塞队列
int consumerAppleCongestionQueue[processNum]; //等待信号量orange的阻塞队列
int shareCongestionQueue[processNum]; //等待信号量mutex的阻塞队列
semaphore empty={1,producerCongestionQueue };
semaphore orange={0,consumerOrangeCongestionQueue };
semaphore apple={0,consumerAppleCongestionQueue };
semaphore mutex={1,shareCongestionQueue};
Process process[processNum]; //进程集合
int fruitcount=0; //水果计数器
int fatherputsnum=0; //父亲一共有多少个水果
//函数声明
void initProcess();
void wakeup(int *pcq);
void block(int pcq[],int code);
void P(semaphore *s,Process *p);
void V(semaphore *s,Process *p);
void Father(Process *p);
void SonOrDaughter(Process *p);
void rr();
void initProcess()//初始化函数
{
int i;
for(i=0;i<processNum;i++)
{
if(i==0)
{
strcpy(process[i].name, "Father");
process[i].roleFlag=1;
}
if(i==1)
{
strcpy(process[i].name, "Son");
process[i].roleFlag=0;
}
if(i==2)
{
strcpy(process[i].name, "Daughter");
process[i].roleFlag=0;
}
process[i].currentState = 1;//就绪状态
process[i].currentStep = 1;//第一步
process[i].code = i + 1;
producerCongestionQueue[i] = 0;
consumerOrangeCongestionQueue[i] = 0;
consumerAppleCongestionQueue[i] = 0;
shareCongestionQueue[i] = 0;
}
}
void wakeup(int *pcq) //唤醒进程操作
{
int code = pcq[0] - 1; //取出队首进程
process[code].currentState = 1; //进程置为就绪态
//当进程被唤醒后继续执行任务
if(process[code].roleFlag == 1) //生产者,父亲
{
if (process[code].currentStep == 2)
{
printf("%s wakes up, can put fruit into plate!\n", process[code].name);
}
if(process[code].currentStep == 3)
{
printf("%s apply for putting fruit into plate sucessfully!\n", process[code].name);
}
}
if(process[code].roleFlag == 0) //消费者,儿子女儿
{
if(process[code].currentStep == 1)
{
printf("%s wakes up,there are fruit in plate!\n", process[code].name);
}
if (process[code].currentStep == 2)
{
printf("%s apply for getting fruit from plate sucessfully!\n", process[code].name);
}
}
process[code].currentStep++;
int i;
for (i=1; (i < processNum) && (pcq[i] != 0); i++) //删除队首进程
{
pcq[i - 1] = pcq[i];
if (pcq[i - 1] > processNum)
{
pcq[i - 1] = 0;
}
}
pcq[i - 1] = 0;
}
void block(int pcq[],int code) //阻塞进程 操作
{
int i;
process[code - 1].currentState = 0; //进程置为阻塞态
for (i = 0; i < processNum; i++)
{
if (!pcq[i])
{
pcq[i] = code;
break;
}
}
}
void P(semaphore *s,Process *p) //P操作
{
s->value -= 1;
if (s->value < 0)
{
p->currentState = 0;
p->currentStep++;
}
if (s->value >= 0) //资源条件满足
{
if (p->roleFlag == 1) { //父亲准备放水果
if (p->currentStep == 2)
{
printf("%s:Plate is empty now.\n", p->name);
}
else if (p->currentStep == 3)
{
printf("%s:Can put fruits into plate.\n", p->name);
}
}
else if (p->roleFlag == 0) //儿子女儿准备取水果
{
if (p->currentStep == 1)
{
printf("%s:There are fruits in plate.\n", p->name);
}
else if (p->currentStep == 2)
{
printf("%2:Can get fruit from plate!\n", p->name);
}
}
p->currentStep++;
}
else if (s->value < 0) //资源条件不满足满足
{
if (p->roleFlag == 1) { //父亲不能放水果,即生产者进程被阻塞
if (p->currentStep == 2) {
printf("%s:Plate is not empty,Block!\n", p->name);
}
else if (p->currentStep == 3) {
printf("%s:Can`t put fruit into plate!\n", p->name);
}
}
else if (p->roleFlag == 0) { //盘子中没有水果,即消费者进程被阻塞
if (p->currentStep == 1) {
printf("%s:No fruits in plate, Block!\n", p->name);
}
else if (p->currentStep == 2) {
printf("%s:Can't get fruit from plate!\n", p->name);
}
}
block(s->pcq, p->code); //阻塞进程
}
}
void V(semaphore *s,Process *p) //V操作
{
s->value += 1;
if (p->roleFlag == 1) //父亲释放对盘子的访问权
{
if (p->currentStep == 5)
{
printf("%s:Release plate access right.\n", p->name);
}
else if (p->currentStep == 6) {
printf("%s:Fruits are put into plate!\n", p->name);
}
}
else if (p->roleFlag == 0) //消费者释放对盘子的访问权
{
if (p->currentStep == 4)
{
printf("%s:Release plate access right.\n", p->name);
}
else if (p->currentStep == 5) {
printf("%s:Fruits are got out from plate.\n", p->name);
}
}
if (s->value <= 0)
{
wakeup(s->pcq);
}
p->currentStep++;
}
void Father(Process *p) //生产者进程,即父亲的进程
{
int orangeOrApple;
switch (p->currentStep)
{
case 1: //1 放水果
orangeOrApple = rand() % 2; //随机数0和1,0表示苹果,1表示橘子
if (orangeOrApple == 0)
strcpy(p->fruit, "Apple");
else
strcpy(p->fruit, "Orange");
p->currentStep++;
break;
case 2: //2 申请空盘子
P(&empty, p);
break;
case 3: //3 申请的盘子访问权
P(&mutex, p);
break;
case 4: //4 将水果放进盘子里
printf("%s puts an %s into plate. This is No.%d fruit.\n", p->name, p->fruit,fruitcount+1);
p->currentStep++;
break;
case 5: //5 释放对盘子的访问权
V(&mutex, p);
break;
case 6:
fruitcount++;
if (!p->fruit) //6 水果已经放入盘子 ,通知两个消费者
V(&apple, p);
else
V(&orange, p);
p->currentStep = 1;
break;
}
}
void SonOrDaughter(Process *p)
{
if (!strcmp(p->name, "Son")) //消费者是儿子
{
switch (p->currentStep)
{
case 1: //1 申请从盘子中拿水果
P(&orange, p);
break;
case 2: //2 申请盘子的访问权
P(&mutex, p);
break;
case 3: //3 从盘子里取水果
printf("%s gets an orange from plate.\n", p->name);
p->currentStep++;
break;
case 4: //4 释放对盘子的访问权
V(&mutex, p);
break;
case 5: //5 已经从盘子里取走水果,通知父亲放水果
V(&empty, p);
p->currentStep = 1;
break;
}
}
if (!strcmp(p->name, "Daughter"))//消费者是女儿,过程类似与儿子
{
switch (p->currentStep)
{
case 1: //1 申请从盘子中拿水果
P(&apple, p);
break;
case 2: //2 申请盘子的访问权
P(&mutex, p);
break;
case 3: //3 从盘子里取水果
printf("%s gets an apple from plate.\n", p->name);
p->currentStep++;
break;
case 4: //4 释放对盘子的访问权
V(&mutex, p);
break;
case 5: //5 已经从盘子里取走水果,通知父亲放水果
V(&empty, p);
p->currentStep = 1;
break;
}
}
}
void rr()
{
printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
printf("Please input the amount of fruits father has is: ");
scanf("%d",&fatherputsnum);
printf("The amount of fruits father has is: %d\n",fatherputsnum);
printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n\n");
printf(">>>>>>>>>>>>>>>>>>>>Process begins......>>>>>>>>>>>>>>>>>>>>>>>\n");
Process *p;
while(fruitcount<fatherputsnum)
{
p = &process[rand() % processNum]; //随机选取进程集合内某一进程
if (!p->currentState)
{ //选取的进程若为阻塞态,重新选取其它可执行进程
continue;
}
if (p->roleFlag) //父亲
{
Father(p);
}
else //儿子女儿
{
SonOrDaughter(p);
}
}
}
int main()
{
printf("\n*******************************************************");
printf("\n* 实验二:生产者—消费者实验模拟 *");
printf("\n*******************************************************\n\n");
printf("\n");
initProcess();
rr();
printf(">>>>>>>>>>>>>>>>>>>>Process ends......>>>>>>>>>>>>>>>>>>>>>>\n");
printf("\n\n");
return 0;
}
实验结果截图(部分)
当水果数为5个时:
实验分析
1.深刻理解了进程之间的同步,进程的同步是指多个进程中发生的事件存在着某种时序关系,必须协同动作、相互配合,从而来共同完成一个任务。也就是说,一个进程运行到某一点时要求另一个伙伴进程为它提供消息,在未获得消息之前,该进程处于等待状态,获得消息后被唤醒进入就绪状态;
2.同时也要区分与进程的互斥的区别,进程的互斥是因为双方竞争同一种资源从而使两者产生了相互制约的互斥关系,是一种间接作用;
3.对问题认真分析,确定问题属于哪种类型,例如本次实验就是典型的生产者-消费者问题,确定临界资源(系统中某些资源一次只允许一个进程使用这样的资源),先用PV原语写出各自逻辑进程,再进一步完善过程;
4.生产者与消费者关心的东西不同,生产者关心的是空的部分,消费者应该关心的是满的部分,因此,有资源竞争的是生产竞争的是生产者和生产者之间,消费者和消费者之间,所以,应该理解为两个不同的临界区,生产者关心生产者的临界区,消费者关心的是消费者的临界区,这样我们可以定义两个互斥信号量,分别管理各自的临界区。
5.进程同步互斥的实现有许多方法,有硬件实现和软件实现,但都不能同时满足空闲让进、忙则等待、有限等待、让权等待,而信号量的提出解决了这些问题,并且可以实现;
6.应当注意,当两个P操作在一起时,他们的顺序至关重要,一个同步P操作与一个互斥P操作的顺序为同步P、操作应该在互斥P操作前面,否则会发生死锁。
以上就是实验所有内容,代码仅供参考!