生产者消费者问题模拟实现(z)

生产者消费者问题模拟实现(z)
生产者消费者问题模拟实现(z)

生产者-消费者实验

1.1实验目的和要求

实验目的

操作系统的基本控制和管理控制都围绕着进程展开,其中的复杂性是由于支持并发和并发机制而引起的。自从操作系统中引入并发程序设计后,程序的执行不再是顺序的,一个程序未执行完而另一个程序便已开始执行,程序外部的顺序特性消失,程序与计算不再一一对应。并发进程可能是无关的,也可能是交互的。然而,交互的进程共享某些变量,一个进程的执行可能会影响其他进程的执行结果,交互的并发进程之间具有制约关系、同步关系。其中典型模型便是生产者-消费者模型。

本实验通过编写和调试生产者-消费者模拟程序,进一步认识进程并发执行的实质,加深对进程竞争关系,协作关系的理解,掌握使用信号量机制与P、V操作来实现进程的同步与互斥。

实验要求

1.用高级语言编写一个程序,模拟多个生产者进程和多个消费者进程并发执行,并采用信号量机制与P、V操作实现进程间同步与互斥。

2.撰写实验报告,报告应包含以下内容:

(1)实验目的;

(2)实验内容;

(3)设计思路;

(4)程序流程图;

(5)程序中主要数据结构和函数说明;

(6)带注释的源程序代码;

(7)程序运行结果及分析;

(8)实验收获与体会。

1.2预备知识

生产者—消费者问题

生产者—消费者问题表述如下:如图3.1所示,有n个生产者和m个消费者,连接在具

有k个单位缓冲区的有界环状缓冲上,故又称有界缓冲问题。生产者不断生成产品,只要缓冲区未满,生产者进程pi所生产的产品就可投入缓冲区;类似的,只要缓冲区非空,消费者进程cj就可以从缓冲区取走并消耗产品。

图 3.1 生产者—消费者问题示意图

著名的生产者—消费者问题(producer-consumer problem)是计算机操作系统中并发进程内在关系的一种抽象,是典型的进程同步问题。在操作系统中,生产者进程可以是计算进程、发送进程,而消费者进程可以是打印进程、接收进程等,解决好生产者—消费者问题就解决了一类并发进程的同步问题。

操作系统实现进程同步的机制称为同步机制,它通常由同步原语组成。不同的同步机制采用不同的同步方法,迄今已设计出多种同步机制,本实验采用最常用的同步机制:信号量及PV操作。

信号量与PV操作

1965年,荷兰计算机科学家E.W.Dijkstra提出新的同步工具——信号量和PV操作,他将交通管制中多种颜色的信号灯管理方法引入操作系统,让多个进程通过特殊变量展开交互。一个进程在某一关键点上被迫停止直至接收到对应的特殊变量值,通过这一措施任何复杂的进程交互要求均可得到满足,这种特殊变量就是信号量(semaphore)。为了通过信号量传送信号,进程可利用P和V两个特殊操作来发送和接收信号,如果协作进程的相应信号仍未到达,则进程被挂起直至信号到达为止。

在操作系统中用信号量表示物理资源的实体,它是一个与队列有关的整型变量。具体实现时,信号量是一种变量类型,用一个记录型数据结构表示,有两个分量:一个是信号量的值,另一个是信号量队列的指针。信号量在操作系统中主要用于封锁临界区、进程同步及维护资源计数。除了赋初值之外,信号量仅能由同步原语PV对其操作,不存在其他方法可以检查或操作信号量,PV操作的不可分割性确保执行的原子性及信号量值的完整性。利用信号量和PV操作即可解决并发进程竞争问题,又可解决并发进程协作问题。

信号量按其用途可分为两种:公用信号量,联系一组并发进程,相关进程均可在此信号量上执行PV操作,用于实现进程互斥;私有信号量,联系一组并发进程,仅允许此信号量所拥有的进程执行P操作,而其他相关进程可在其上执行V操作,初值往往为0或正整数,多用于并发进程同步。

信号量的定义为如下数据结构:

typedef struct semaphore

{

int value; //信号量的值

struct pcb *list; //信号量队列的指针

}

信号量说明:semaphore s;

P、V操作原语描述如下:

(1)P(s):s.value--;若s.value≥0,则执行P(s)的进程继续执行;若s.value<0,则执行P(s)的进程被阻塞,并把它插入到等待信号量s的阻塞队列中。

(2)V(s):s.value++;若s.value≤0,则执行V(s)的进程从等待信号量s的阻塞队列中唤醒头一个进程,然后自己继续执行。若s.value>0 ,则执行V(s)的进

程继续执行;

信号量实现互斥

信号量和PV操作可用来解决进程互斥问题。为使多个进程能互斥地访问某临界资源,只需为该资源设置一互斥信号量mutex,并置初值为1,然后将各进程访问该资源的临界区置于P(mutex)和V(mutex)操作之间即可。

用信号量和PV操作管理并发进程互斥进入临界区的一般形式为:

semaphore mutex;

mutex = 1;

cobegin

process Pi() /*i = 1,2, …,n */

{

P(mutex);

/*临界区*/

V(mutex);

}

coend

当有进程在临界区中时,mutex的值为0或负值,否则mutex值为1,因为只有一个进程,可用P操作把mutex减至0,故可保证互斥操作,这时试图进入临界区的其它进程会因执行P(mutex)而被迫等待。mutex的取值范围是1~-(n-1),表明有一个进程在临界区内执行,最多有n-1个进程在信号量队列中等待。

信号量解决生产者—消费者问题

信号量和PV操作不仅可以解决进程互斥问题,而且是实现进程同步的有力工具。在协作进程之间,一个进程的执行依赖于协作进程的信息或消息,在尚未得到来自协作进程的信号或消息时等待,直至信号或消息到达时才被唤醒。

生产者—消费者问题是典型的进程同步问题,对于生产者进程:生产一个产品,当要送入缓冲区时,要检查是否有空缓冲区,若有,则可将产品送入缓冲区,并通知消费者进程;否则,等待;对于消费者进程:当它去取产品时,要看缓冲区中是否有产品可取,若有则取走一个产品,并通知生产者进程,否则,等待。这种相互等待,并互通信息就是典型的进程同步。

因此应该设两个同步信号量:信号量empty表示可用的空缓冲区的数目,初值为k;信号量full表示可以使用产品的数目,初值为0。

缓冲区是一个临界资源,必须互斥使用,所以另外还需要设置一个互斥信号量mutex,其初值为1。

用信号量机制解决生产者—消费者问题可描述如下:

item B[k];

semaphore empty;empty=k; //可以使用的空缓冲区数

semaphore full; full=0; //缓冲区内可以使用的产品数

semaphore mutex;mutex=1; //互斥信号量

int in=0;//放入缓冲区指针

int out=0; //取出缓冲区指针

cobegin

process producer_i() process consumer()

{ {

While(true)While(true)

{ {

produce();P(full);

P(empty);P(mutex);

P(mutex);take from B[out];

append to B[in];out = (out+1)%k;

in = (in+1)%k;V(mutex);

V(mutex);V(empty);

V(full);consume();

}}

} }

Coend

程序中的P(mutex)和V(mutex)必须成对出现,夹在两者之间的代码段是临界区;施加于信号量empty和full上的PV操作也必须成对出现,但分别位于不同的程序中。在生产者消

费者问题中,P操作的次序是很重要的,如果把生产者进程中的两个P操作交换次序,那么,当缓冲区中存满k件产品时,生产者又生产一件产品,在它欲向缓冲区存放时,将在P(empty)上等待,由于此时mutex=0,它已经占有缓冲区,这时消费者预取产品时将停留在P(mutex)上而得不到使用缓冲区的权力。这就导致生产者等待消费者取走产品,而消费者却在等待生产者释放缓冲区的占有权,这种互相之间的等待永远不可能结束。所以,在使用信号量和PV操作实现进程同步时,特别要当心P操作的次序,而V操作的次序无关紧要。一般来说,用于互斥的信号量上的P操作总是在后面执行。

1.3生产者消费者问题模拟实现

实验内容

考虑一个系统中有n个进程,其中部分进程为生产者进程,部分进程为消费者进程,共享具有k个单位的缓冲区。

现要求用高级语言编写一个程序,模拟多个生产者进程和多个消费者进程并发执行的过程,并采用信号量机制与P、V操作实现生产者进程和消费者进程间同步以及对缓冲区的互斥访问。利用信号量机制解决此问题的算法见3.2.4所示。

实验指导

1.设计提示

(1)本实验并不需要真正创建生产者和消费者进程,每个进程用一个进程控制块(PCB)表示。PCB数据结构如下:

typedef struct Process//进程PCB

{

char name[10];//进程名

int roleFlag;//进程类型(1:生产者0: 消费者)

int currentState;//进程状态(1: 可运行态0: 阻塞态)

int currentStep;//断点

int data;//临时数据

int code;//进程编号

}Process;

(2)程序中应指定缓冲区的数目,进程总个数等,现考虑共有4个生产者和消费者进程,缓冲区数目是两个,定义如下所示:

#define dataBufferSize 2//缓冲区数目

#define processNum 4//进程数量(生产者、消费者进程总数目)

struct DataBuffer //缓冲区

{

int buffer[dataBufferSize];

int count;//当前产品数量

} dataBuffer;

(3)为解决生产者-消费者问题需设两个同步信号量:信号量empty表示可用的空缓冲区的数目,初值为缓冲区数目;信号量full表示可以使用产品的数目,初值为0。缓冲区是一个临界资源,必须互斥使用,所以另外还需要设置一个互斥信号量mutex,其初值为1。信号量定义和说明如下所示:

typedef struct Seamphore //信号量

{

int value;//信号量的值

int *pcq;//信号量队列指针

} Seamphore;

int producerCongestionQueue[processNum];//等待信号量empty的阻塞队列

int consumerCongestionQueue[processNum];//等待信号量full的阻塞队列

int shareCongestionQueue[processNum];//等待信号量mutex的阻塞队列

Seamphore empty={dataBufferSize,producerCongestionQueue};

Seamphore full={0,consumerCongestionQueue};

Seamphore mutex={1,shareCongestionQueue};

(4)为模拟多个生产者和多个消费者进程并发执行的过程,首先根据进程总个数产生若干生产者和若干消费者进程,然后随机调度一个处于就绪态的进程,判断是生产者还是消费者,然后执行不同的代码,为模拟并发执行,进程每执行一步操作就中断执行,再调度其他进程运行,在被中断进程的PCB中记录了中断的位置,等到下次被调度执行时则从此位置继续执行。

(5)生产者进程执行时分为6步,如下所示:

void produce(Process *p) //生产者进程执行代码

{

switch (p->currentStep) {

case 1://1 生产产品

p->data = rand()%1000;

printf("%20s: 生产一个产品%d!\n", p->name, p->data);

p->currentStep++;

break;

case 2://2 申请空缓冲

P(&empty, p);

break;

case 3://3 申请访问缓冲区

P(&mutex, p);

break;

case 4://4 将产品送入缓冲区

push(p->data);

printf("%20s: 将产品%d正送入缓冲区!\n", p->name, p->data);

p->currentStep++;

break;

case 5://5 释放缓冲区访问权

V(&mutex, p);

break;

case 6://6 产品已送入缓冲区,产品数量加1

V(&full, p);

p->currentStep = 1;

break;

}

}

(6)消费者进程执行时也分为6步,如下所示:

void consume(Process *p) //消费者进程执行代码

{

switch (p->currentStep) {

case 1://1 申请从缓冲区取出产品

P(&full, p);

break;

case 2://2 申请访问缓冲区

P(&mutex, p);

break;

case 3://3 从缓冲区中取出产品p->data = pop();

printf("%20s: 从缓冲区中正取出产品%d!\n", p->name, p->data);

p->currentStep++;

break;

case 4://4 释放缓冲区访问权

V(&mutex, p);

break;

case 5://5 已从缓冲区取出一个产品,空缓冲区数量加1

V(&empty, p);

break;

case 6://6 消费产品

printf("%20s: 消费产品%d!\n", p->name, p->data);

p->currentStep = 1;

break;

}

}

(6)为对生产者进程和消费者进程并发执行的过程进行分析,理解信号量和P、V操作在进程同步和互斥机制中的运用,要求进程每执行一步都输出每一步的执行情况。

2.程序流程图

(1)程序流程图如图3.2所示:

图3.2 程序流程图

(2)生产者进程和消费者进程执行时各有6步操作,执行一个操作后会被中断,下次再被调度执行时接着执行下一操作。生产者进程流程图如图3.3所示,消费者进程流程图如图3.4所示。

开始

1:生产产品

2:P(empty)

3:P(mutex)

4:将产品送入缓冲区5:V(mutex)

6:V(full)

开始

1:P(full)

2:P(mutex)

3:从缓冲区取一个产品4:V(mutex)

5:V(full)

6:消费产品

图2.2 生产者进程流程图图2.3 消费者进程流程图

程序示例

#include "stdio.h"

#include "time.h"

#include "stdlib.h"

#include "string.h"

#include "windows.h"

#define dataBufferSize 2//缓冲区数目

#define processNum 4//进程数量(生产者、消费者进程总数目)

typedef struct Seamphore //信号量

{

int value;//信号量的值

int *pcq;//信号量队列指针

} Seamphore;

int producerCongestionQueue[processNum];//等待信号量empty的阻塞队列

int consumerCongestionQueue[processNum];//等待信号量full的阻塞队列

int shareCongestionQueue[processNum];//等待信号量mutex的阻塞队列

Seamphore empty={dataBufferSize,producerCongestionQueue}; // empty:空缓冲区数目Seamphore full={0,consumerCongestionQueue}; // full:缓冲区内可用的产品Seamphore mutex={1,shareCongestionQueue}; //mutex:互斥信号量

struct DataBuffer //缓冲区

{

int buffer[dataBufferSize];

int count;//当前产品数量

} dataBuffer;

typedef struct Process //进程PCB

{

char name[10];//进程名

int roleFlag;//进程类型(1: 生产者0: 消费者)

int currentState;//进程状态(1: 就绪态0: 阻塞态)

int currentStep;//断点

int data;//临时数据

int code;//进程编号

}Process;

Process process[processNum];//进程集合

void moveDataForward()

{

int i;

for (i = 0; i < dataBuffer.count; i++)

dataBuffer.buffer[i] = dataBuffer.buffer[i+1];

}

void push(int data) //产品送入缓冲区

{

dataBuffer.buffer[dataBuffer.count++] = data;

}

int pop() //从缓冲区取出产品

{

int data = dataBuffer.buffer[0];

dataBuffer.count--;

moveDataForward();

return data;

}

void initProcess() {//初始化进程集合

int i;

char digitTemp[5];

srand(time(NULL));

for (i = 0; i < processNum; i++) {

process[i].roleFlag = rand()%2;// 随机指定当前进程为生产者或消费者if (process[i].roleFlag)

strcpy(process[i].name, "生产者");

else

strcpy(process[i].name, "消费者");

strcat(process[i].name, itoa(i+1, digitTemp, 10));

process[i].currentState = 1;

process[i].currentStep = 1;

process[i].code = i + 1;

producerCongestionQueue[i] = 0;

consumerCongestionQueue[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("%20s: 该进程被唤醒!申请空缓冲区成功!\n", process[code].name);

} else if (process[code].currentStep == 3) {

printf("%20s: 该进程被唤醒!申请访问缓冲区成功!\n",

process[code].name);

}

} else if (process[code].roleFlag == 0) {//消费者

if (process[code].currentStep == 1) {

process[code].data = pop();

printf("%20s: 该进程被唤醒!申请取产品%d成功!\n", process[code].name, process[code].data);

} else if (process[code].currentStep == 2) {

printf("%20s: 该进程被唤醒!申请访问缓冲区成功!\n",

process[code].name);

}

}

process[code].currentStep++;

for (int 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 sleep(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(Seamphore *s, Process *p) {//模拟P操作

s->value -= 1;

if (s->value>= 0) {

if (p->roleFlag == 1) {//生产者

if (p->currentStep == 2) {

printf("%20s: 申请空缓冲区成功!\n", p->name);

} else if (p->currentStep == 3) {

printf("%20s: 申请访问缓冲区成功!\n", p->name);

}

} else if (p->roleFlag == 0) {//消费者

if (p->currentStep == 1) {

printf("%20s: 申请取出产品成功!\n", p->name);

} else if (p->currentStep == 2) {

printf("%20s: 申请访问缓冲区成功!\n", p->name);

}

}

p->currentStep++;//下一步

} else if (s->value < 0) {

if (p->roleFlag == 1) {//生产者

if (p->currentStep == 2) {

printf("%20s: 无空缓冲区, 该进程被阻塞!\n", p->name);

} else if (p->currentStep == 3) {

printf("%20s: 其他进程正在访问缓冲区, 该进程被阻塞!\n", p->name);

}

} else if (p->roleFlag == 0) {//消费者

if (p->currentStep == 1) {

printf("%20s: 无产品可取, 该进程被阻塞!\n", p->name);

} else if (p->currentStep == 2) {

printf("%20s: 其他进程正在访问缓冲区, 该进程被阻塞!\n", p->name);

}

}

sleep(s->pcq, p->code);//阻塞进程

}

}

void V(Seamphore *s, Process *p) {//模拟V操作

s->value += 1;

if (p->roleFlag == 1) {//生产者

if (p->currentStep == 5) {

printf("%20s: 释放缓冲区访问权!\n", p->name);

} else if (p->currentStep == 6) {

printf("%20s: 产品已送入缓冲区,产品数量增加!\n", p->name);

}

} else if (p->roleFlag == 0) {//消费者

if (p->currentStep == 4) {

printf("%20s: 释放缓冲区访问权!\n", p->name);

} else if (p->currentStep == 5) {

printf("%20s: 产品已取出,空缓冲区数量增加!\n", p->name);

}

}

if (s->value<= 0) {

wakeup(s->pcq);

}

p->currentStep++;

}

void produce(Process *p) {//模拟生产者进程

switch (p->currentStep) {

case 1://1 生产产品

p->data = rand()%1000;

printf("%20s: 生产一个产品%d!\n", p->name, p->data);

p->currentStep++;

break;

case 2://2 申请空缓冲区

P(&empty, p);

break;

case 3://3 申请访问缓冲区

P(&mutex, p);

break;

case 4://4 将产品送入缓冲区

push(p->data);

printf("%20s: 将产品%d正送入缓冲区!\n", p->name, p->data);

p->currentStep++;

break;

case 5://5 释放缓冲区访问权

V(&mutex, p);

break;

case 6://6 产品已送入缓冲区,产品数量加1

V(&full, p);

p->currentStep = 1;

break;

}

}

void consume(Process *p) {//模拟消费者进程

switch (p->currentStep) {

case 1://1 申请从缓冲区取出产品

P(&full, p);

break;

case 2://2 申请访问缓冲区

P(&mutex, p);

break;

case 3://3 从

缓冲区中取出产品

p->data = pop();

printf("%20s: 从缓冲区中正取出产品%d!\n", p->name, p->data);

p->currentStep++;

break;

case 4://4 释放缓冲区访问权

V(&mutex, p);

break;

case 5://5 已从缓冲区取出一个产品,空缓冲区数量加1

V(&empty, p);

break;

case 6://6 消费产品

printf("%20s: 消费产品%d!\n", p->name, p->data);

p->currentStep = 1;

break;

}

}

void rr() {//模拟进程调度

Process *p;

while(1) {

p = &process[rand()%processNum];//随机选取进程集合内某一进程

if (!p->currentState) {//选取的进程若为阻塞态,重新选取其它可执行进程

continue;

}

if (p->roleFlag) {//1: 生产者0: 消费者

produce(p);

} else {

consume(p);

}

Sleep(100);

}

}

void deal() {

printf("\t\t生产者消费者算法模拟\n\n");

initProcess();

rr();

}

int main () {

deal();

return 0;

}

运行结果及分析

1.运行结果

程序经编译运行后,输出如下结果:

生产者消费者算法模拟

消费者2: 无产品可取, 该进程被阻塞!

生产者3: 生产一个产品344!

生产者3: 申请空缓冲区成功!

生产者1: 生产一个产品723!

生产者1: 申请空缓冲区成功!

生产者3: 申请访问缓冲区成功!

生产者3: 将产品344正送入缓冲区!

生产者3: 释放缓冲区访问权!

生产者1: 申请访问缓冲区成功!

生产者1: 将产品723正送入缓冲区!

生产者3: 产品已送入缓冲区,产品数量增加!

消费者2: 该进程被唤醒!申请取产品344成功!

消费者4: 无产品可取, 该进程被阻塞!

生产者1: 释放缓冲区访问权!

消费者2: 申请访问缓冲区成功!

消费者2: 从缓冲区中正取出产品723!

生产者3: 生产一个产品924!

消费者2: 释放缓冲区访问权!

生产者1: 产品已送入缓冲区,产品数量增加!

消费者4: 该进程被唤醒!申请取产品723成功!

生产者1: 生产一个产品510!

生产者1: 无空缓冲区, 该进程被阻塞!

消费者2: 产品已取出,空缓冲区数量增加!

生产者1: 该进程被唤醒!申请空缓冲区成功!

生产者1: 申请访问缓冲区成功!

消费者4: 其他进程正在访问缓冲区, 该进程被阻塞!

生产者3: 无空缓冲区, 该进程被阻塞!

生产者1: 将产品510正送入缓冲区!

生产者1: 释放缓冲区访问权!

消费者4: 该进程被唤醒!申请访问缓冲区成功!

消费者4: 从缓冲区中正取出产品723!

消费者4: 释放缓冲区访问权!

消费者4: 产品已取出,空缓冲区数量增加!

生产者3: 该进程被唤醒!申请空缓冲区成功!

生产者1: 产品已送入缓冲区,产品数量增加!

生产者1: 生产一个产品450!

消费者2: 消费产品723!

生产者1: 无空缓冲区, 该进程被阻塞!

消费者2: 申请取出产品成功!

消费者2: 申请访问缓冲区成功!

消费者2: 从缓冲区中正取出产品723!

消费者4: 消费产品723!

消费者4: 无产品可取, 该进程被阻塞!

消费者2: 释放缓冲区访问权!

消费者2: 产品已取出,空缓冲区数量增加!

生产者1: 该进程被唤醒!申请空缓冲区成功!

生产者3: 申请访问缓冲区成功!

生产者1: 其他进程正在访问缓冲区, 该进程被阻塞!

生产者3: 将产品924正送入缓冲区!

消费者2: 消费产品723!

生产者3: 释放缓冲区访问权!

生产者1: 该进程被唤醒!申请访问缓冲区成功!

生产者1: 将产品450正送入缓冲区!

生产者3: 产品已送入缓冲区,产品数量增加!

消费者4: 该进程被唤醒!申请取产品723成功!

生产者1: 释放缓冲区访问权!

生产者1: 产品已送入缓冲区,产品数量增加!

消费者4: 申请访问缓冲区成功!

消费者4: 从缓冲区中正取出产品723!

消费者4: 释放缓冲区访问权!

生产者3: 生产一个产品138!

消费者4: 产品已取出,空缓冲区数量增加!

生产者1: 生产一个产品881!

生产者1: 申请空缓冲区成功!

生产者3: 无空缓冲区, 该进程被阻塞!

消费者4: 消费产品723!

……

2.简要分析

在本次程序运行时,创建了两个生产者进程和两个消费者进程,生产者进程编号为1、3,消费者进程编号为2、4。缓冲区数目是2个。

从本次运行情况来看,消费者2先请求取产品,因缓冲区内无产品,此时消费者2被阻塞;生产者3和生产者1各生产一个产品,生产者3先将产品送入缓冲区后,唤醒了消费者2去取产品;此时消费者4也请求取产品,因无产品可取而被阻塞;然后生产者1将产品送

入了缓冲区,消费者4被唤醒;生产者1又生产了一个产品,由于此时消费者2和消费者4还未将产品取出,已无空缓冲区,所以生产者1被阻塞,只能等待;等到消费者2取出产品后,生产者1被唤醒;但由于此时消费者4正在缓冲区取产品,生产者1由于不能访问缓冲区而被阻塞;等消费者4完成缓冲区操作后,生产者1将产品送入缓冲区;在消费者4取产品过程中,生产者3又生产了一件产品,由于无空缓冲区而被阻塞,等消费者4取出产品,有空缓冲区时被唤醒……生产者和消费者进程如此不断执行。

从程序运行过程来看,实现了生产者进程和消费者进程的同步,同时也实现了各进程对缓冲区的互斥访问。

若再次运行此程序,可以发现显示输出的结果会不同,那是由于创建的生产者进程和消费者进程是随机创建的,另外调度进程时也是随机调度的。

生产者与消费者问题(Java)

package Table; public class Message { public static int id; public String content; public String getContent() { return content; } public void setContent(String content) { this.content = content; } public int getId() { return id; } public void setId(int id) { Message.id = id; } } package Table; import java.util.ArrayList; import java.util.List; public class Queue { List queue = new ArrayList(); /** 队列中message对象的最大值,默认为10 */ int maxMessageNum = 10; public synchronized void produce(Message message) { /**synchronized为关键字,表示在任何一个线程要访问缓冲区时都会 检查有无其他线程访问此段内容,有的话则等待,无的话则直接访问**/ this.notifyAll(); while (queue.size() == maxMessageNum) { System.out.println(Thread.currentThread().getName() + "the desk is full, and the chef want to relax"); try { this.wait(); } catch (InterruptedException e) { e.printStackTrace();

操作系统实验报告--实验一--进程管理

实验一进程管理 一、目的 进程调度是处理机管理的核心内容。本实验要求编写和调试一个简单的进程调度程序。通过本实验加深理解有关进程控制块、进程队列的概念,并体会和了解进程调度算法的具体实施办法。 二、实验内容及要求 1、设计进程控制块PCB的结构(PCB结构通常包括以下信息:进程名(进程ID)、进程优先数、轮转时间片、进程所占用的CPU时间、进程的状态、当前队列指针等。可根据实验的不同,PCB结构的内容可以作适当的增删)。为了便于处理,程序中的某进程运行时间以时间片为单位计算。各进程的轮转时间数以及进程需运行的时间片数的初始值均由用户给定。 2、系统资源(r1…r w),共有w类,每类数目为r1…r w。随机产生n进程P i(id,s(j,k),t),0<=i<=n,0<=j<=m,0<=k<=dt为总运行时间,在运行过程中,会随机申请新的资源。 3、每个进程可有三个状态(即就绪状态W、运行状态R、等待或阻塞状态B),并假设初始状态为就绪状态。建立进程就绪队列。 4、编制进程调度算法:时间片轮转调度算法 本程序用该算法对n个进程进行调度,进程每执行一次,CPU时间片数加1,进程还需要的时间片数减1。在调度算法中,采用固定时间片(即:每执行一次进程,该进程的执行时间片数为已执行了1个单位),这时,CPU时间片数加1,进程还需要的时间片数减1,并排列到就绪队列的尾上。 三、实验环境 操作系统环境:Windows系统。 编程语言:C#。 四、实验思路和设计 1、程序流程图

2、主要程序代码 //PCB结构体 struct pcb { public int id; //进程ID public int ra; //所需资源A的数量 public int rb; //所需资源B的数量 public int rc; //所需资源C的数量 public int ntime; //所需的时间片个数 public int rtime; //已经运行的时间片个数 public char state; //进程状态,W(等待)、R(运行)、B(阻塞) //public int next; } ArrayList hready = new ArrayList(); ArrayList hblock = new ArrayList(); Random random = new Random(); //ArrayList p = new ArrayList(); int m, n, r, a,a1, b,b1, c,c1, h = 0, i = 1, time1Inteval;//m为要模拟的进程个数,n为初始化进程个数 //r为可随机产生的进程数(r=m-n) //a,b,c分别为A,B,C三类资源的总量 //i为进城计数,i=1…n //h为运行的时间片次数,time1Inteval为时间片大小(毫秒) //对进程进行初始化,建立就绪数组、阻塞数组。 public void input()//对进程进行初始化,建立就绪队列、阻塞队列 { m = int.Parse(textBox4.Text); n = int.Parse(textBox5.Text); a = int.Parse(textBox6.Text); b = int.Parse(textBox7.Text); c = int.Parse(textBox8.Text); a1 = a; b1 = b; c1 = c; r = m - n; time1Inteval = int.Parse(textBox9.Text); timer1.Interval = time1Inteval; for (i = 1; i <= n; i++) { pcb jincheng = new pcb(); jincheng.id = i; jincheng.ra = (random.Next(a) + 1); jincheng.rb = (random.Next(b) + 1); jincheng.rc = (random.Next(c) + 1); jincheng.ntime = (random.Next(1, 5)); jincheng.rtime = 0;

生产者消费者问题设计与实现

操作系统课程设计任务书

目录

1.选题背景 生产者消费者问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。解决生产者/消费者问题的方法可分为两类:(1)采用某种机制保护生产者和消费者之间的同步;(2)在生产者和消费者之间建立一个管道。第一种方式有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式。第二种管道缓冲区不易控制,被传输数据对象不易于封装等,实用性不强。因此本文只介绍同步机制实现的生产者/消费者问题。 同步问题核心在于:如何保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问。Java语言在多线程编程上实现了完全对象化,提供了对同步机制的良好支持。在Java中一共有四种方法支持同步,其中前三个是同步方法,一个是管道方法。 2.设计思路 .生产者—消费者问题是一种同步问题的抽象描述。 计算机系统中的每个进程都可以消费或生产某类资源。当系统中某一进程使用某一资源时,可以看作是消耗,且该进程称为消费者。 而当某个进程释放资源时,则它就相当一个生产者 3.过程论述 首先,生产者和消费者可能同时进入缓冲区,甚至可能同时读/写一个存储单元,将导致执行结果不确定。这显然是不允许的。所以,必须使生产者和消费者互斥进入缓冲区。即某时刻只允许一个实体(生产者或消费者)访问缓冲区,生产者互斥消费者和其他任何生产者。 其次,生产者不能向满的缓冲区写数据,消费者也不能在空缓冲区中取数据,即生产者与消费者必须同步。当生产者产生出数据,需要将其存入缓冲区之前,首先检查缓冲区中是否有“空”存储单元,若缓冲区存储单元全部用完,则生产者必须阻塞等待,直到消费者取走一个存储单元的数据,唤醒它。若缓冲区内有“空”存储单元,生产者需要判断此时是否有别的生产者或消费者正在使用缓冲区,若是有,则阻塞等待,否则,获得缓冲区的使用权,将数据存入缓冲区,释放缓冲区的使用权。消费者取数据之前,首先检查缓冲区中是否存在装有数据的存储单元,若缓冲区为“空”,则阻塞等待,否则,判断缓冲区是否正在被使用,

生产者与消费者

重庆交通大学 《计算机操作系统》课程设计报告 班级:计软专业 2013 级 2 班 姓名: 学号: 课程设计题目:生产者/消费者与FCFS 所属课程:计算机操作系统 实验室(中心):语音大楼801 指导教师:刘洋 完成时间: 2015 年 12 月 5 日 信息科学与工程学院课程设计成绩单

课程名称:计算机操作系统指导教师:刘洋

重庆交通学院信息科学与工程学院课程设计任务书

生产者/消费者与FCFS 一、内容提要 操作系统是计算机的核心软件,是计算机专业学生的专业必修课。进程同步问题是计算机操作系统中的重点内容,而生产者-消费者问题是进程同步问题中的经典,它是计算机中相互合作进程关系的一种抽象,该问题具有很大的代表性和使用价值。 在计算机系统中,很多问题都可以归结为生产者与消费者问题,提别是在通讯和控制系统中。因此,对该类问题的研究是非常必要的。一般而言,我们把提供给某种资源的进程(线程)称之为生产者,二吧消耗资源的进程(线程)称之为消费者。在大多数情况下,生产者和消费者的数目都是多于一个的。下面以多个进程(线程)共享一有界缓冲池为例来说明。 如图,左端箭头表示生产者,右端箭头表示消费者,设P 1、P 2 、…、P K 是k 个想缓冲池装入数据的进程(线程)(生产者)C 1、C 2 、…、C m 是m个冲缓冲池 中取出数据进行处理的进程(线程)(消费者),假设对任何P i 每次向缓冲区 中申请一空白缓冲区,并把数据装入其中;而对于常见的情况是任何C i 每次都从缓冲池中取出一满缓冲区的内容,并进行相应的处理,并把缓冲区清空。而每次生产者装入数据和消费者取出数据都是相同的(这是最常见的情况)。针对以上进程(线程)通信,如不对生产者进程(线程)和消费者(线程)的操作进行限制,则可能破坏数据的完整性。一种情况是一个生产者进程(线程)正在装入数据到缓冲区时,另外的生产者进程(线程)可能同时把数据写入相同区域,造成数据破坏,另一种情况是一个生产者进程(线程)正在装入数据到缓冲区时,另外的消费者进程(线程)可能读入该区域数据,由于数据未写完,从而造成消

生产者消费者问题模拟实现(z)

生产者-消费者实验 1.1实验目的和要求 1.1.1实验目的 操作系统的基本控制和管理控制都围绕着进程展开,其中的复杂性是由于支持并发和并发机制而引起的。自从操作系统中引入并发程序设计后,程序的执行不再是顺序的,一个程序未执行完而另一个程序便已开始执行,程序外部的顺序特性消失,程序与计算不再一一对应。并发进程可能是无关的,也可能是交互的。然而,交互的进程共享某些变量,一个进程的执行可能会影响其他进程的执行结果,交互的并发进程之间具有制约关系、同步关系。其中典型模型便是生产者-消费者模型。 本实验通过编写和调试生产者-消费者模拟程序,进一步认识进程并发执行的实质,加深对进程竞争关系,协作关系的理解,掌握使用信号量机制与P、V操作来实现进程的同步与互斥。 1.1.2实验要求 1.用高级语言编写一个程序,模拟多个生产者进程和多个消费者进程并发执行,并采用信号量机制与P、V操作实现进程间同步与互斥。 2.撰写实验报告,报告应包含以下内容: (1)实验目的; (2)实验内容; (3)设计思路; (4)程序流程图; (5)程序中主要数据结构和函数说明; (6)带注释的源程序代码; (7)程序运行结果及分析; (8)实验收获与体会。 1.2预备知识 1.2.1生产者—消费者问题 生产者—消费者问题表述如下:如图3.1所示,有n个生产者和m个消费者,连接在具

有k个单位缓冲区的有界环状缓冲上,故又称有界缓冲问题。生产者不断生成产品,只要缓冲区未满,生产者进程pi所生产的产品就可投入缓冲区;类似的,只要缓冲区非空,消费者进程cj就可以从缓冲区取走并消耗产品。 图 3.1 生产者—消费者问题示意图 著名的生产者—消费者问题(producer-consumer problem)是计算机操作系统中并发进程内在关系的一种抽象,是典型的进程同步问题。在操作系统中,生产者进程可以是计算进程、发送进程,而消费者进程可以是打印进程、接收进程等,解决好生产者—消费者问题就解决了一类并发进程的同步问题。 操作系统实现进程同步的机制称为同步机制,它通常由同步原语组成。不同的同步机制采用不同的同步方法,迄今已设计出多种同步机制,本实验采用最常用的同步机制:信号量及PV操作。 1.2.2信号量与PV操作 1965年,荷兰计算机科学家E.W.Dijkstra提出新的同步工具——信号量和PV操作,他将交通管制中多种颜色的信号灯管理方法引入操作系统,让多个进程通过特殊变量展开交互。一个进程在某一关键点上被迫停止直至接收到对应的特殊变量值,通过这一措施任何复杂的进程交互要求均可得到满足,这种特殊变量就是信号量(semaphore)。为了通过信号量传送信号,进程可利用P和V两个特殊操作来发送和接收信号,如果协作进程的相应信号仍未到达,则进程被挂起直至信号到达为止。 在操作系统中用信号量表示物理资源的实体,它是一个与队列有关的整型变量。具体实现时,信号量是一种变量类型,用一个记录型数据结构表示,有两个分量:一个是信号量的值,另一个是信号量队列的指针。信号量在操作系统中主要用于封锁临界区、进程同步及维护资源计数。除了赋初值之外,信号量仅能由同步原语PV对其操作,不存在其他方法可以检查或操作信号量,PV操作的不可分割性确保执行的原子性及信号量值的完整性。利用信号量和PV操作即可解决并发进程竞争问题,又可解决并发进程协作问题。 信号量按其用途可分为两种:公用信号量,联系一组并发进程,相关进程均可在此信号量上执行PV操作,用于实现进程互斥;私有信号量,联系一组并发进程,仅允许此信号量所拥有的进程执行P操作,而其他相关进程可在其上执行V操作,初值往往为0或正整数,多用于并发进程同步。

操作系统进程管理系统设计实验报告

实验报告说明书设计名称:操作系统课程设计 实验:进程调度设计 学生姓名: 专业:网络工程 班级: 08级一班 学号: 指导教师:雷晓平王东黄营杨跃武 日期: 2011年 6月 19日

课程设计任务书 网络工程专业 08 年级 1 班 一、具体要求 本课程设计共2周,采取集中方式。 ㈠主要设计内容 1、进程调度 2、存储管理 3、文件管理 ㈡操作系统分项设计 1、设计一:进程管理系统设计 目的与要求:本设计的目的是加深对进程概念及进程管理各部分内容的理解;熟悉进程管理中主要数据结构的设计及进程调度算法、进程控制机构、同步机构及通讯机构的实施。 要求设计一个允许n个进程并发运行的进程管理模拟系统。该系统包括有简单的进程控制、同步与通讯机构,其进程调度算法可任意选择。每个进程用一个PCB表示,其内容根据具体情况设置。各进程之间有一定的同步关系(可选)。系统在运行过程中应能显示或打印各进程的状态及有关参数的变化情况,以便观察诸进程的运行过程及系统的管理过程。 具体详见:设计任务书1--进程调度算法.doc 2、设计二:存贮器管理系统设计 目的与要求:本设计的目的是使学生熟悉存贮器管理系统的设计方法;加深对所学各种存贮器管理方案的了解;要求采用一些常用的存贮器分配算法,设计一个存贮器管理模拟系统并调试运行。模拟环境应尽量接近真实。 具体详见:设计任务书2--内存分区管理模拟.doc 3、设计三:虚拟存储器管理系统设计 本设计的目的是通过设计一个简单的虚拟存储器管理系统来模拟实际的页面调度算法与过程,以掌握这种有用的技术。要求将其输入/输出处理程序编成一个独立的进程模块并与其它请求输入/输出的进程并发运行。并要求加入设备管理子模块。 具体分析为:页面调度算法主要有FIFO、最近最少使用调度算法(LRU)、最近最不常用调度算法(LFU)、最佳算法(OPT)等。题目要求: ①实现三种算法:1、先进先出;2、OPT;3、LRU ②页面序列从指定的文本文件(TXT文件)中取出 ③输出:第一行:每次淘汰的页面号,第二行:显示缺页的总次数 4、设计四:文件管理系统设计 目的与要求:本设计的目的是通过设计和调试一个简单的外部文件系统,主要是模拟文件操作,,使学生对主要文件操作命令的实质和执行过程有比较深入的了解,掌握它们的基本实施方法。 基本要求如下: 实现三种算法:先来先服务、最短寻道优先、电梯算法 磁道服务顺序从指定的文本文件(TXT文件)中取出 输出:第一行:磁道的服务顺序;第二行:显示移动总道数

操作系统实验报告生产者消费者问题

操作系统课程设计 一.实验目标 完成N个生产者和M个消费者线程之间的并发控制,N、M不低于30,数据发送和接收缓冲区尺寸不小于20个(每个产品占据一个)。 其中生产者线程1、3、5、7、9生产的产品供所有奇数编号的消费者线程消费,只有所有奇数编号的消费者线程都消费后,该产品才能从缓冲区中撤销。 其中生产者线程2、4、6、8、10生产的产品所有偶数编号的消费者线程都可消费,任一偶数编号消费者线程消费该消息后,该产品都可从缓冲区中撤销。 其中11-20号生产者线程生产的产品仅供对应编号的消费者线程消费。 其他编号生产者线程生产的产品可由任意的消费者线程消费。 每个生产线程生产30个消息后结束运行。如果一个消费者线程没有对应的生产者线程在运行后,也结束运行。所有生产者都停止生产后,如果消费者线程已经

没有可供消费的产品,则也退出运行。 二.实验原理 2.1原理 生产者与消费者线程采用posix互斥锁机制进行互斥进入各自的代码段,只有采用互斥锁临界区代码段才可以不被打扰的执行;同步机制采用的是posix条件变量pthread_cond_wait和pthraed_cond_signal进行同步的。 线程间的通信采用的是共享内存机制。(注:所有的共享内存块是在进程里建立的,线程只需链接上各自的共享内存块即可,每一块共享内存的大小是100). 在这里共享内存设置成一个100的数组。 具体实施:(1)为1.3.5.7.9建立一个共享内存1号,1.3.5.7.9生产者线程生产的产品都放入这块共享内存缓冲区,所有奇数的消费者线程要消费的话,只需在消费者线程中链接上这块共享内存,就可以直接消费1.3.5.7.9生产者线程生产的产品。 (2)为2.4.6.8.10建立一块共享内存2号。2.4.6.8.10生产的产品都放入2号共享内存缓冲区,所有的偶数的消费者线程只要链接上2号缓冲区,就可以消费2.4.6.8.10生产的产品。当偶数消费者线程消费产品后,产品即可从缓冲区撤销,方法是在消费线程里将消费的产品在共享内存数组里置0。 (3)为11--20的每一对生产者消费者线程建立一块共享内存,编号11--20. 11--20号的消费者线程能链接各自的共享内存缓冲区或奇数或偶数共享内存缓冲区,即11--20号的生产者生产的产品只能被对应的消费者消费而11-20的奇数消费者可以消费缓冲区1的产品,偶数消费者可消费缓冲区2的产品。 (4)为21--30号的生产者消费者线程只建立一块共享内存21号,21--30号生产者生产的产品都放入21号缓冲区,所有的消费者线程只要链接上21号共享内存,就可以消费21--30号生产者生产的产品。 用于控制线程是否结束的方法是:设置一个全局变量t,在生产者线程里进行t++,在生产者线程里当t达到10时(注:为了很好的测试程序,本应该在生产者生产30个产品时菜结束线程,这里设置成了10),就break跳出while()循环,这样线程自然就终止。同样在消费者线程里,当t达到10时,这里不用t++,就跳出while()循环,消费者线程自然就终止。这样设计满足了,当生产者生产30个产品时就终止生产者线程,生产者线程终止消费者线程也得终止的要求。 生产者从文件so.txt读取数据进行生产,这个文件里的数据是一连串的字符从a--z的组合,没有空格或其他字符。文件内容的格式没有特殊要求。

操作系统生产者与消费者问题实验报告

《操作系统》实验报告 生产者和消费者的问题 一、实验目的 1.掌握基本的同步与互斥的算法,理解基本的生产者与消费者的模型。 2.学习使用Windows 2000/XP中基本的同步对象,掌握相关的API的使用方法。 3.了解Windows 2000/XP中多线程的并发执行机制,线程间的同步和互斥。 二、实验的内容及其要求 1.实验内容 以生产者/消费者模型为根据,在Windows 2000环境下创建一个控制台进程,在改进程中创建n个线程模拟生产者和消费者,实现进程(线程)的同步与互斥。 2.实验要求 ①学习并理解生产者/消费者模型及其同步/互斥规则 ②学习了解Windows同步对象及其特性 ③熟悉实验环境,掌握相关API的使用方法 ④设计程序,实现生产者/消费者进程(线程)的同步与互斥 ⑤提交实验报告 三、实验的时间安排 1.实验前,先到图书馆或上网百度了解有关生产者/消费者模型的相关知识,建立生产者/消费者模型的基本概念。 2.利用13周、15周、17周的上机时间编写和调试程序代码。 3.利用其他课余时间来分析实验的最终结果并完成相关的实验报告。 四、实验的环境 1.硬件条件:普通计算机一台 2.软件条件:①操作系统:Windows 2000/XP ②开发语言:VC++ 本实验是在Windows 2000+VC6.0环境下实现的,利用Windows SDK提供的系统接口(API)完成程序的功能。实验在Windows下安装VC后进行,因为VC是一个集成开发环境,其中包含了Windows SDK所有工具和定义,所以安装了VC后就不用特意安装SDK了。实验中所用的API(应用程序接口),是操作系统提供的用来进行应用程序设计的系统功能接口。要使用这些API,需要包含对这些函数进行说明的SDK 头文件,最常见的就是windows.h。一些特殊的API调用还需要包含其他的头文件。 五、正文 1.程序结构图:

生产者与消费者问题(附源码)

操作系统实验报告 专业网络工程班级08102 学号姓名 课程名称操作系统学年2010-2011 学期下 课程类别专业必修■限选□任选□实践□实验时间2010年11月3日 实验名称 实验一:生产者与消费者问题 实验目的和要求 全面理解生产者与消费者问题模型,掌握解决该问题的算法思想,正确使用同步机制。 实验软硬件要求 Pentium ||| 450以上CPU 64MB以上内存 WINDOWS XP Visual C++6.0 实验内容、方法和步骤(可附页) 问题描述:一组生产者向一组消费者提供商品,共享一个有界缓冲池,生产者向其中放入商品,消费者从中取得商品。假定这些生产者和消费者互相等效,只要缓冲池未满,生产者可将商品送入缓冲池;只要缓冲池未空,消费者可从缓冲池取走一商品。 功能要求:根据进程同步机制,编写一个解决上述问题的程序,可显示缓冲池状态、放商品、取商品等过程。 具体参数:3个生产者进程,2个消费者进程; 缓冲区单元个数N=4; 在本程序中是缓冲区中的数从0变为1表示模拟生产一个产品,消费时则将对应缓冲区内的1变为0,为模拟消费一个产品。 实验结果(可附页) 见截图 小结 这次多线程的操作系统实验,使我对线程的概念以及多线程程序中线程间的运行有了更深的认识,同时也让我的编程能力得到了一定的提高。 这次做的用多线程实现生产者与消费者模型的实验,由于我的编程能力基础比较差,对线程也是一无所知,所以一开始觉得无从下手,但幸好老师给了充足的时间,我通过看网上找的视频资料以及请教同学才渐渐地有了一点概念,然后我试着从网上下了一些多线程的程序分析里面的语句,基本弄懂了多线程的原理。 评定成绩:批阅教师:年月日

进程管理器的模拟实现

衡阳师范学院《操作系统》课程设计 题目:进程管理器的模拟实现系别:计算机科学系 专业:物联网工程 班级:1206班 学生姓名:郑晓娟 学号:12450218 指导老师:王玉奇 完成日期:2014年12月28日

目录 一、需求分析 (3) 二、概要设计 (3) 三、详细设计 (4) 1.进程PCB结构体的定义 (4) 2.创建模块 (4) 3.查看模块 (5) 4.换出模块 (6) 5.杀死模块 (8) 四、程序调试 (10) 五、总结分析 (14)

一、需求分析 有时候我们需要去了解我们电脑的运行情况,掌握和管理它的进程,并对其异常情况给予操作和控制。进程管理器就像我们了解和控制自己电脑运作的一个窗口,通过这个窗口我们可以查看到所有进程的运行情况,并对运行的进程加以管理和控制。在本课程设计中,进入模拟进程系统后,可以根据请求选择“创建进程”创建新的进程。还可以选择“查看进程“来查看当前正在运行的进程,以及该进程的相关的信息,其中包括进程的pid,进程优先级,进程大小和进程的内容,我们可以通过这些来了解计算机中每个进程的使用状况。选择“换出进程”,可以挂起某个正在运行的进程。选择“撤销进程”,可以停止某个正在运行的程序,并且释放其占用的资源。选择“退出进程”,可以退出进程模拟系统。 二、概要设计 程序流程:

三、详细设计 (1)进程PCB结构体的定义 struct jincheng_type{ //定义表示进程信息的结构体int pid; //进程ID int youxian; //优先级 int daxiao; //大小 int zhuangtai; //进程的状态,这里用0表示没有建立或被杀死,1表示执行,2表示换出 int neirong; //内容 }; (2)创建模块 void create() //函数--创建一个新进程 { if(shumu>=20) printf("\n内存已满,请先换出或杀死进程\n") //判断内存空间是否已满 else{ for(int i=0;i<20;i++) if(neicun[i].zhuangtai==0) break; //选出空着的内存单元给新进程使用 printf("\n请输入新进程pid\n"); //输入新进程ID存至选出的内存单元 scanf("%d",&(neicun[i].pid));

1实验1:生产者消费者问题

1实验1:生产者消费者问 题 -标准化文件发布号:(9556-EUATWK-MWUB-WUNN-INNUL-DDQTY-KII

福建农林大学金山学院实验报告 系(教研室):专业:计算机科学与技术年级: 实验课程:生产者与消费者实验姓名:学号: 实验室号:1#608 计算机号:实验时间:指导教师签字:成绩: 实验1:生产者消费者问题 一、实验目的 生产者消费者问题是操作系统中经典的同步和互斥问题。通过实验,要求学生掌握两者之间的同步信号量和互斥信号量的使用,更深刻了解临界资源、同步和互斥的概念。 二、实验要求 1.一组生产者通过一个具有N个缓冲区的缓冲池循环不断地向一组消费者提供 产品。 2.建一个队列, 队列的长度由n记录, 定义两个指针, 分别指向队列的头和尾消费 者从头指针读取数据,每读取一个数据把n--,生产者把数据写入尾指针, 每写 入一个数据就n++,当n=N的时候生产者暂停写入数据。 3.注意:缓冲池队列,用互斥锁保护。 三、实验内容和原理 1.分别画出生产者和消费者的流程图

2.针对生产者和消费者问题,可以分为哪几种情况,使用了哪些原语分别代表什 么意思过程如何阐述哪些进程之间存在同步,哪些进程之间存在互斥。 3.缓冲区是否为临界资源是否可以循环使用通过什么来实现举例说明(可画图) 四、实验环境 1. 硬件:PC机; 2. 软件:Windows操作系统、。 五、算法描述及实验步骤 #include <> #include const unsigned short SIZE_OF_BUFFER = 10; unsigned short ProductID = 0; unsigned short ConsumeID = 0; unsigned short in = 0;

进程管理模拟实验指导书09

进程管理模拟系统实验指导书2 一、实验目的 学习进程管理的设计与实现,学习和运用操作系统原理,设计一个操作系统子系统的模拟系统。通过该系统的设计调试可增加对操作系统实现的感知性。同时可发挥团队协作精神和个人创造能力。使同学们对操作系统学习有一个实现的尝试和创新的思维。 二、实验规则 1.每组设计一个模拟系统(共100分) 2.每人设计系统中的一部分(满分60分) 3.集体调试创意(满分40分) 三、实验要求 1.进程管理功能以进程调度为主要功能。以进程控制为辅助功能。 2.体现操作系统原理中进程调度算法和进程控制算法。按照操作系统原理设计。 3.结构化设计。设计时构建出模块结构图并存于文件中。模块化实现,对每一功能,每一操作使用模块、函数、子程序设计方法实现。 4.进程以PCB为代表。队列、指针用图示。每一步功能在桌面上能显示出来。 5.系统应具有排错功能,对可能出现的错误应具有排查功能和纠错能力。 6.界面自行设计,语言自行选择。(可用VC/C++/C/C#语言,也可用你会的其他语言,甚至还可用PPT) 7.每人的设计功能都能表现或说明出来。 8.进程以队列法组织,对不同进程调度算法: FIFO队列或PRI队列或rotate(轮转队列)用同一个进程序列组织,对阻塞队列可设置一个,也可设多个。 9.因为是模拟系统,所以要显示每个功能和操作结果。显示应力求清晰、易读和一目了然(一屏),最好能用汉字,否则可用英语或汉语拼音。 10.操作方便,使用便捷。可视化程度高。 11.设计出系统后,还需要写出(介绍系统采用的语言、支撑平台、小组成员及分工。如何安装、如何启动、如何操作) 12.每组需写一份课程设计报告,内容包括:课程设计内容,课程设计设计思路,课程设计结构图,及分工内容、介绍。 13. 实验结果演示验收后,将可在任何环境下运行的可执行文件和系统说明书一起存盘并交盘。(可合组一张盘),上标:班级、组号、姓名。 14. 实验结束后从中选出优秀作品,介绍给大家。 四、系统功能 1.创建进程:主要创建PCB,并在创建后显示PCB及所在RL队列。内容包括①标识数(按产生顺序产生),②进程名(字母序列),③优先数(随机产生),④进程状态,⑤队列指针(可用数字或图表示),⑥其它(可自定义:如运行时间、家族等)。创建进程的个数可人工设定,或可自动设定,也可两者兼有。 2.撤销进程:撤销进程主要显示PCB的消失和队列的变化。 3.进程队列的组织:进程队列可对创建的所有进程变化队形:可组织成FIFO队列,也可组织成PRI队列;或rotate队列,对队列有插入、移出的功能,也有在队列中某位置插入删除功能。 4.显示功能:模拟系统在整个演示过程中都需要可视化,因此显示功能非常重要,要求对队列、PCB每次操作前后予以显示,以表示操作功能的实施效果。

实验报告五 生产者和消费者问题

实验报告五 ——生产者和消费者问题 姓名:丛菲学号:20100830205 班级:信息安全二班一、实习内容 ?1、模拟操作系统中进程同步和互斥 ?2、实现生产者和消费者问题的算法实现 二、实习目的 ?1、熟悉临界资源、信号量及PV操作的定义与物理意义 ?2、了解进程通信的方法 ?3、掌握进程互斥与进程同步的相关知识 ?4、掌握用信号量机制解决进程之间的同步与互斥问题 ?5、实现生产者-消费者问题,深刻理解进程同步问题 三、实习题目 ?在Linux操作系统下用C实现经典同步问题:生产者—消费者,具体要求如下: (1)一个大小为10的缓冲区,初始状态为空。 (2)2个生产者,随机等待一段时间,往缓冲区中添加数据,若缓冲区已满,等待消 费者取走数据之后再添加,重复10次。 (3)2个消费者,随机等待一段时间,从缓冲区中读取数据,若缓冲区为空,等待生 产者添加数据之后再读取,重复10次。 ?提示 本实验的主要目的是模拟操作系统中进程同步和互斥。在系统进程并发执行异步推进的过程中,由于资源共享和进程间合作而造成进程间相互制约。进程间的相互制约有两种不同的方式。 (1)间接制约。这是由于多个进程共享同一资源(如CPU、共享输入/输出设备)而引起的,即共享资源的多个进程因系统协调使用资源而相互制约。 (2)直接制约。只是由于进程合作中各个进程为完成同一任务而造成的,即并发进程各自的执行结果互为对方的执行条件,从而限制各个进程的执行速度。 生产者和消费者是经典的进程同步问题,在这个问题中,生产者不断的向缓冲区中写入数据,而消费者则从缓冲区中读取数据。生产者进程和消费者对缓冲区的操作是互斥,即当前只能有一个进程对这个缓冲区进行操作,生产者进入操作缓冲区之前,先要看缓冲区是否已满,如果缓冲区已满,则它必须等待消费者进程将数据取出才能写入数据,同样的,消费者进程从缓冲区读取数据之前,也要判断缓冲

进程管理系统设计

目录 一、设计系统目的 (1) (1) 三、系统分析 (1) 四、系统设计 (1) 五、程序设计流程图 (5) 六、源程序清单 (5) 七、调试过程中的问题及系统测试情况 (12) 1 (12) 2、系统测试结果 (12) 八、系统设计总结 (14)

一、设计系统目的 本设计的目的是加深对进程概念及进程管理各部分内容的理解,熟悉进程管理中主要数据结构的设计及进程调度算法、进程控制机构、同步机构及通讯机构实施。 设计一个允许n个进程并发运行的进程管理模拟系统。该系统包括有简单的进程控制、同步与通讯机构,其进程调度算法可任意选择。每个进程用一个PCB表示,其内容根据具体情况设置。各进程之间有一定的同步关系可选。系统在运行过程中应能显示或打印各进程的状态及有关参数的变化情况,以便观察诸进程的运行过程及系统的管理过程。 三、系统分析 1、进程控制和同步等 可以控制进程的就绪执行和阻塞三种状态等基本功能。 2、进程调度 调度算法采用的是先来先服务算法。 3 输出的内容包括—就绪的进程队列,当前cup正在执行的进程,被阻塞的进程队 列。 四、系统设计 本系统所包括的数据结构是对列类 本系统由于需要输出就绪队列,执行队列,阻塞对列,故需要利用对列来实现。 最好利用队列类来实现,这样可以充分利用类的继承来简化程序。 class queue { public:

queue():rear(NULL),front(NULL){}; ~queue(); void enqueue( char &item); char dequeue(); void del(char item); void display(); int find(char item); int isempty(){return front==NULL;} private: queuenode *front,*rear; }; queue::~queue() { queuenode * p; while(front!=NULL) { p=front;front=front->link;delete p; } 队列类的成员函数的定义如下: void queue::enqueue(char &item) { if(front==NULL) front=rear=new queuenode(item,NULL); else rear=rear->link=new queuenode(item,NULL); } char queue::dequeue() {

操作系统课程设计生产者-消费者问题附代码

枣庄学院 信息科学与工程学院 课程设计任务书题目:生产者-消费者问题的实现 姓名: 学号: 专业:计算机科学与技术 课程:操作系统 指导教师:刘彩霞职称:讲师完成时间:2012年5月----2012 年6月 枣庄学院信息科学与工程学院制 课程设计任务书及成绩评定

目录 第1章引言 (1) 1.1 设计背景 (1) 1.2 问题分类 (1) 1.3 解决方案 (1) 第2章设计思路及原理 (2) 第3章程序详细设计 (3) 3.1程序模块设计 (3) 3.2程序代码结构 (5) 第4章实验结果 (7) 第5章实验总结 (8) 附录:实验代码 (9)

第1章引言 1.1 设计背景 生产者-消费者问题是一个经典的进程同步问题,该问题最早由Dijkstra 提出,用以演示他提出的信号量机制。在同一个进程地址空间内执行的两个线程。生产者线程生产物品,然后将物品放置在一个空缓冲区中供消费者线程消费。消费者线程从缓冲区中获得物品,然后释放缓冲区。当生产者线程生产物品时,如果没有空缓冲区可用,那么生产者线程必须等待消费者线程释放出一个空缓冲区。当消费者线程消费物品时,如果没有满的缓冲区,那么消费者线程将被阻塞,直到新的物品被生产出来。 1.2 问题分类 根据缓冲区的个数、大小以及生产者消费者的个数可以分为以下几类: 1.单缓冲区(适合单或多生产消费者); 2.环行多缓冲区(或无穷缓冲区)单生产消费者; 3.环行多缓冲区多生产消费者; 1.3 解决方案 1.用进程通信(信箱通信)的方法解决; 2.进程消息缓冲通信; 3.进程信箱通信;

第2章设计思路及原理 设计了两个主要函数:生产者函数、消费者函数; 设计了三个信号量:full信号量,判断缓冲区是否有值,初值为0; empty信号量,判断缓冲区是否有空缓冲区,初值为缓 冲区数; mutex信号量作为互斥信号量,用于互斥的访问缓冲区。 生产者函数通过执行P操作信号量empty减1,判断缓冲区是否有空。有空则互斥的访问缓冲区并放入数据,然后释放缓冲区,执行V操作,信号量full 加1。 消费者函数执行P操作,信号量full减1,判断是否有数据,有则互斥的访问缓冲区并取走数据,然后释放缓冲区,执行V操作,empty信号量加1。

生产者与消费者实验报告

生产者和消费者实验报告 【实验目的】 1.加深对进程概念的理解,明确进程和程序的区别。 2.进一步认识并发执行的实质。 3.验证用信号量机制实现进程互斥的方法。 4.验证用信号量机制实现进程同步的方法。 【实验要求】 用c语言编程搭建“生产者和消费者”经典进程通信问题的环境。要求程序运行时,按任意键停止,显示当前系统的各个参数的值。提交实验报告,以及相关程序列表。打包成附件上传。 【实验环境】 Visual C++6.0 【实验内容】 1.了解经典同步问题“生产者和消费者” 生产者与消费者可以通过一个环形缓冲池联系起来,环形缓冲池由几个大小相等的缓冲块组成,每个缓冲块容纳一个产品。每个生产者可不断地每次往缓冲池中送一个生产产品,而每个消费者则可不断地每次从缓冲池中取出一个产品。指针i和指针j分别指出当前的第一个空缓冲块和第一个满缓冲块。 2.分析和理解 (1)既存在合作同步问题,也存在临界区互斥问题 合作同步:当缓冲池全满时,表示供过于求,生产者必须等待,同时唤醒消费者;当缓冲池全空时,表示供不应求,消费者应等待,同时唤醒生产者。 互斥:缓冲池显然是临界资源,所在生产者与消费都要使用它,而且都要改变它的状态。 (2)基于环形缓冲区的生产者与消费者关系形式描述: 公用信号量mutex:初值为1,用于实现临界区互斥 生产者私用信号量empty:初值为n,指示空缓冲块数目 消费者私用信号量full:初值为0,指示满缓冲块数目 整型量i和j初值为0,i指示首空缓冲块序号,j指示首满缓冲块序号 (3)PV原语 var mutex,empty,full:semaphore; i,j:integer;buffer:array[0...n-1] of item; i:=j:=1; Procedure producer; begin while true do begin

操作系统-实验三-进程管理-实验报告

计算机与信息工程学院实验报告 一、实验内容 1.练习在shell环境下编译执行程序 (注意:①在vi编辑器中编写名为sample.c的c语言源程序 ②用linux自带的编译器gcc编译程序,例如:gcc –o test sample.c ③编译后生成名为test.out的可执行文件; ④最后执行分析结果;命令为:./test) 注意:linux自带的编译程序gcc的语法是:gcc –o 目标程序名源程序名,例如:gcc –o sample1 sample1.c,然后利用命令:./sample 来执行。如果仅用“gcc 源程序名”,将会把任何名字的源程序都编译成名为a.out的目标程序,这样新编译的程序会覆盖原来的程序,所以最好给每个源程序都起个新目标程序名。 2.进程的创建 仿照例子自己编写一段程序,使用系统调用fork()创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示“a”,子进程分别显示字符“b”和“c”。观察记录屏幕上的显示结果,并分析原因。 3.分析程序 实验内容要在给出的例子程序基础上,根据要求进行修改,对执行结果进行分析。二、

实验步骤 1. 利用fork()创建一个小程序 (1)编写程序 #include main () { int i=5; pid_t pid; pid=fork(); for(;i>0;i--) { if (pid < 0) printf("error in fork!"); else if (pid == 0) printf("i am the child process, my process id is %d and i=%d\n",getpid(),i); else printf("i am the parent process, my process id is %d and i=%d\n",getpid(),i); } for(i=5;i>0;i--) { if (pid < 0) printf("error in fork!"); else if (pid == 0) printf("the child process, my process id is %d and i=%d\n",getpid(),i); else printf("the parent process, my process id is %d and

操作系统课程设计(进程管理)

操作系统课程设计报告 题目: 专业: 班级: 姓名: 学号: 指导老师: 年月日

《操作系统》课程设计任务书 一、课程设计题目(任选一个题目) 1.模拟进程管理 2.模拟处理机调度 3.模拟存储器管理 4.模拟文件系统 5.模拟磁盘调度 二、设计目的和要求 1.设计目的 《操作系统原理》课程设计是计算机科学与技术专业实践性环节之一,是学习完《操作系统原理》课程后进行的一次较全面的综合练习。其目的在于加深对操作系统的理论、方法和基础知识的理解,掌握操作系统结构、实现机理和各种典型算法,系统地了解操作系统的设计和实现思路,培养学生的系统设计能力,并了解操作系统的发展动向和趋势。 2.基本要求: (1)选择课程设计题目中的一个课题,独立完成。 (2)良好的沟通和合作能力 (3)充分运用前序课所学的软件工程、程序设计、数据结构等相关知识 (4)充分运用调试和排错技术 (5)简单测试驱动模块和桩模块的编写 (6)查阅相关资料,自学具体课题中涉及到的新知识。 (7)课题完成后必须按要求提交课程设计报告,格式规范,内容详实。 三、设计内容及步骤 1.根据设计题目的要求,充分地分析和理解问题,明确问题要求做什么。

2.根据实现的功能,划分出合理的模块,明确模块间的关系。 3.编程实现所设计的模块。 4.程序调试与测试。采用自底向上,分模块进行,即先调试低层函数。能够熟练掌握调试工具的各种功能,设计测试数据确定疑点,通过修改程序来证实它或绕过它。调试正确后,认真整理源程序及其注释,形成格式和风格良好的源程序清单和结果; 5.结果分析。程序运行结果包括正确的输入及其输出结果和含有错误的输入及其输出结果。 6.编写课程设计报告; 设计报告和实验报告要求:A4纸和实验报告册,详细设计部分主要叙述本人的工作内容 设计报告的格式: (1)封面(题目、指导教师、专业、班级、姓名、学号) (2)设计任务书 (3)目录 (4)需求分析 (5)概要设计 (6)详细设计(含主要代码) (7)调试分析、测试结果 (8)用户使用说明 (9)附录或参考资料 四、进度安排 设计在第四学期的第1-18周(共18课时)进行,时间安排如下:

相关文档
最新文档