操作系统实验 生产者/消费者模型
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信号量实现环形队列生产者消费者模型)