操作系统实验 生产者/消费者模型

Posted 狱典司

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了操作系统实验 生产者/消费者模型相关的知识,希望对你有一定的参考价值。

  • 注:本文上半部分为原理,下半部分为实现

生产者 / 消费者模型:
生产者消费者模型具体来讲,就是在一个系统中,存在生产者和消费者两种角色,它们通过内存缓冲区进行通信。

概况描述:
· 两个或者更多的进程(线程)共享同一个缓冲区;
· 其中一个或多个进程(线程)作为“生产者”向缓冲区中添加数据;
· 另一个或者多个进程(线程)作为“消费者”从缓冲区中取走数据;
· 生产者和消费者必须互斥的使用缓冲区;
· 缓冲区空时,消费者不能读取数据;
· 缓冲区满时,生产者不能添加数据。

优点:
(1) 解耦:
· 因由缓冲区做交互中介,生产者和消费者并不直接相互调用;
· 其实就是把生产者和消费者之间的耦合解开,变成了生产者和缓冲区消费者和缓冲区之间的耦合。

(2) 支持并发:
若消费者直接从生产者拿数据,消费者需要等待生产者生产数据;
同理,生产者需要等待消费者消费数据。

而在生产者/消费者模型中:
· 生产者和消费者是两个独立的并发主体。
· 生产者把制造出来的数据添加到缓冲区,即可去生产下一个数据。
· 同理,消费者从缓冲区中读取数据,不需要等待生产者生产数据。
· 如此,生产者和消费者就可以并发的执行。

(3)支持忙闲不均:
· 假设没有缓冲区,且消费者和生产者的速度不匹配,则会造成CPU的浪费。
· 生产者/消费者模型使得生产者/消费者的处理能力达到一个动态的平衡。

—————————————————————————————

试验任务:

①由用户指定要产生的进程及其类别,存入进入就绪队列。

②调度程序从就绪队列中提取一个就绪进程运行。
③如果申请的资源被阻塞则进入相应的等待队列,则调度程序调度就绪队列中的下一个进程。
④进程运行结束时,会检查对应的等待队列,
⑤激活相应等待队列中的进程进入就绪队列。
⑥运行结束的进程进入over链表(用于记录进程完成顺序)。
⑦重复这一过程直至就绪队列为空。

⑧程序询问是否要继续?如果要转至①开始执行,否则退出程序。

  • 每个进程由一个进程控制块(PCB)表示。进程控制块可以包含如下信息:进程类型标号、进程系统号、进程状态、进程链指针等等。
  • 系统开辟了一个缓冲区,大小由buffersize指定。
  • 程序中的链队列 / 链表:
    1.一个就绪队列
    2.两个等待队列:生产者等待队列和消费者等待队列
    3.一个over链表,用于收集已经运行结束的进程
    注:初始化时还可以添加生产者进程队列和消费者进程队列以便观察

C语言代码实现:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define MAX_SIZE 20
#define BUFFER_SIZE 3 


typedef struct PCB_Type{
	int type; 
	int pid; // 0 ~ 9
	int state;//0 or 1 
	int priority;//1 ~ 10  本实验中暂时用不到
	int time;//1 ~ 50   本实验中暂时用不到
	struct PCB_Type *next;
	 
}PCB,*Linklist;


//初始化10个PCB
//type产生随机数0或1,表示进程类型,1为生产者进程,0为消费者进程;
//state为1代表就绪状态,state为0代表等待状态 
void Inet_list(PCB *producer_list , PCB *comsumer_list){
	PCB *p = producer_list;
	PCB *c = comsumer_list;
	int i=0;
	while(i<11){
		PCB *tmp = (PCB *)malloc(sizeof(PCB)); 
		tmp->type = rand()%2;//随机生成进程类型 
		tmp->pid = i;
		tmp->state = 1;//根据实验要求,所有进程一开始都进入就绪队列 
		tmp->priority = rand()%10+1;
		tmp->time = rand()%50+1;
		//printf("id:%d || type:%d\\n",tmp->pid,tmp->type); //生成测试语句 
		if(tmp->type == 1)
		{
			p->next = tmp;
		    p = tmp;
		    p->next = NULL;
		}
		
		if(tmp->type == 0){
			c->next = tmp;
		    c = tmp;
		    c->next = NULL;
		}	
		i++;	
	}
}

void show_list(PCB *p){
	PCB *q = p->next;
	if(!q){
		printf("空!\\n");
	}else{
	while(q){
		printf("%d  ",q->pid);
		q = q->next;
	}
	putchar('\\n');
  }
}

void sort_ready(PCB *producer_list, PCB *comsumer_list, PCB *ready_list){  //初始化就绪队列 
	PCB *p = producer_list->next;
	PCB *c = comsumer_list->next;
	PCB *tail = ready_list;
	tail->next = NULL;
	PCB *t;
	while(p && c){
		if(p->pid < c->pid){
			t = p;
			p = p->next;
		}else{
			t = c;
			c = c->next;
		}
		t->next = NULL;
		tail->next = t;
		tail = t;
	}
	if(p) tail->next = p;
	if(c) tail->next = c;
}

//执行就绪队列中的程序 
//如果申请的进程被阻塞: 则进入相应的等待队列,调度程序调度就绪队列中的下一个进程。 
void Do(PCB *ready_list , PCB *p_wait_list, PCB *c_wait_list, PCB *over_list, int *buffer_data, int *buffer_blank){
PCB *r = ready_list->next;
PCB *o = over_list;
PCB *cw = c_wait_list;
PCB *pw = p_wait_list;

if(r->type == 1){
	printf(" %d进程为生产者进程!",r->pid); 
	
	if(*buffer_blank>0){ //如果缓存区还有空位 
		printf("成功加入缓存区!\\n"); 
		//printf("缓存区数据量:%d\\n",buffer_data);
		--*buffer_blank;
		++*buffer_data;
	
		
		ready_list->next = r->next;//加入over list 
	    r->next=NULL;
	    
	    
	    while(o->next  != NULL){
	    	o = o->next;
	    } //找到over_list的最末尾位置 
     	o->next = r; 
	} 
	
	else{ //如果缓存区没有空位 
		printf("阻塞!加入生产者等待队列!\\n"); 
		ready_list->next = r->next;
	    r->next=NULL;
	    r->state = 0;//等待状态下state为0; 
	    while(pw->next  != NULL){
	    	pw = pw->next;
	    } //找到生产者等待队列的最末尾位置 
     	pw->next = r; 		
	}
  }

if(r->type == 0){
	printf(" %d进程为消费者进程!",r->pid); 
	
	if(*buffer_data>0){ //如果缓存区有数据缓存 
		printf("成功从缓存区取出一条数据!\\n"); 
		++*buffer_blank;
		--*buffer_data;
		
		ready_list->next = r->next; //加入over list 
	    r->next=NULL;
	    while(o->next  != NULL){
	    	o = o->next;
	    } //找到over_list的最末尾位置 
     	o->next = r; 
	} 
	
	else{ //如果缓存区没有数据 
		printf("阻塞!加入消费者等待队列!\\n"); 
		ready_list->next = r->next;
	    r->next=NULL;
	    r->state = 0;//等待状态下state为0; 
	    while(cw->next  != NULL){
	    	cw = cw->next;
	    } //找到消费者等待队列最末尾位置 
     	cw->next = r; 		
	}
  }
  printf("检查等待队列......");  //进程结束,检查等待队列
  
  if(*buffer_data>0 && c_wait_list->next){ //如果缓存区有数据,激活消费者等待队列的首个进程 
   printf("激活消费者等待队列的首个进程,加入就绪队列!\\n"); 
   PCB *cw = c_wait_list->next;
   PCB *r = ready_list->next;
   c_wait_list->next = cw->next;
   ready_list->next = cw;
   cw->next = r;
   cw->state = 1;//就绪状态state为1; 
  } 
  
  
  else if(*buffer_blank>0 && p_wait_list->next){ //如果缓存区有存储空间,激活消费者等待队列的首个进程 
   printf("激活生产者等待队列的首个进程,加入就绪队列!\\n"); 
   PCB *pw = p_wait_list->next;
   PCB *r = ready_list->next;
   p_wait_list->next = pw->next;
   ready_list->next = pw;
   pw->next = r;
   pw->state = 1;//就绪状态state为1; 
  } 
  
  else{
  	printf("等待队列中没有需要激活的进程!\\n"); 
  }
}

void line(){
	printf("--------------------------------------------------\\n");
}


int main(){
	int judge = 1; //看到最后几行代码即可明白
while(judge==1){

	int buffer_data = 0;   //初始化缓存区 
    int buffer_blank = BUFFER_SIZE;
	PCB *producer_list = (PCB *)malloc(sizeof(PCB));  //生产者队列 (头节点不存信息) 
    producer_list->next = NULL;
    
    PCB *p_wait_list = (PCB *)malloc(sizeof(PCB));  //生产者等待队列 (头节点不存信息) 
    p_wait_list->next = NULL;

    PCB *comsumer_list = (PCB *)malloc(sizeof(PCB));  //消费者队列 (头节点不存信息) 
    comsumer_list->next = NULL;
    
    PCB *c_wait_list = (PCB *)malloc(sizeof(PCB));  //消费者队列 (头节点不存信息) 
    c_wait_list->next = NULL;
    
    PCB *ready_list = (PCB *)malloc(sizeof(PCB));  //就绪队列 (头节点不存信息) 
    ready_list->next = NULL;
    
    PCB *over_list = (PCB *)malloc(sizeof(PCB));  //over链表 (头节点不存信息) 
    over_list->next = NULL;

    Inet_list(producer_list,comsumer_list);   // 初始化生产者和消费者队列 
     
    printf("初始状态:\\n");
    printf("生产者进程:");       
	show_list(producer_list);
	printf("消费者进程:"); 
	show_list(comsumer_list);
	sort_ready(producer_list, comsumer_list, ready_list); //初始化就绪队列 
	printf("就绪队列:");
	show_list(ready_list);
	
	
	while(ready_list->next) {
		line();
	
	Do(ready_list , p_wait_list, c_wait_list, over_list, &buffer_data, &buffer_blank);
	
	printf("就绪队列:");
	show_list(ready_list);
	printf("生产者等待队列:");
	show_list(p_wait_list);
	printf("消费者等待队列:");
	show_list(c_wait_list);
	printf("已完成进程:");
	show_list(over_list);
	
	printf("缓存区数据量:%d\\n",buffer_data);
	printf("缓存区剩余存储量:%d\\n",buffer_blank); 
		
	}
	line();
	printf("是否继续?\\n");
	printf("1 ————是,继续执行程序!\\n");
	printf("0 ————否,退出程序!\\n");
	printf("请输入您的选择:") ;
	int temp;
	scanf("%d",&temp);
	judge = temp; 
	if(judge == 0){
		printf("退出程序!");
	}
}
	return 0;
}


实现结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

以上是关于操作系统实验 生产者/消费者模型的主要内容,如果未能解决你的问题,请参考以下文章

Linux多线程_(Posix信号量实现环形队列生产者消费者模型)

JAVA模拟生产者与消费者实例

Linux多线程_(线程同步,基于阻塞队列的生产者消费者模型)

综合运用: C++11 多线程下生产者消费者模型详解(转)

实验四生产者和消费者

实验四生产者和消费者