进 程 通 信
指导老师: 夏 建
一、实验题目
进程通信上机实验(消息缓冲通信)
二、算法思想
1、在进程管理(调度)实验基础上,加入进程通信的功能,采用消息缓冲通信机制进行通信。
2、P1发送数据给P2,P2排序后发送给P3,P3接收数据并输出。
3、 要发送的数据内容由操作者实时输入。
三、小组分工:
四、算法程序
1、 缓冲区结构
typedef struct node
{int id;
int size;
char text[100];
struct node *next;
}buftype;
2、 进程控制块PCB
struct {int id;
char status;
int waiter1;
int priority;
char stack[5];
int sm;
buftype *front;
buftype *rear;
}pcb[4];
3 .发送、接收、排序程序算法流程
(1)发送函数 send()
程序:
void send(int rec_id,char a[],int n){
buftype *p;
p=(buftype *)malloc(sizeof(buftype));
p->id=rec_id; //接收消息进程id
p->size=n; //消息的大小
strcpy(p->text,a); //将消息拷贝到节点之中
p->next=0;
if(pcb[rec_id].sm<=0)//如果消息缓冲队列空..就将p放在链首
{
pcb[rec_id].front=p;
}
else
pcb[rec_id].rear->next=p;//如果不空..放在队尾..rear指针后移一位
pcb[rec_id].rear=p;//队尾指针指向新加入的缓冲区
pcb[rec_id].sm++; //接收进程的消息个数+1
}
(2)接收函数 receive()
程序:
int receive(int rec_id,char b[])
{
buftype *p;
if(pcb[rec_id].sm<=0){
printf("no message!!\n");
return 0;
}else{
p=pcb[rec_id].front;
pcb[rec_id].sm--;
pcb[rec_id].front=pcb[rec_id].front->next;//取出来后..front指针后移一位
strcpy(b,p->text);
free(p);
if(pcb[rec_id].front==NULL)
pcb[rec_id].rear=NULL;
return 1;
}
}
(3)排序函数 sort()
调用参数:a[ ] 待排序数组,n 数据个数;
程序:
void sort(char a[],int n)
{
int i,j;
char temp;
for(i=0;i<n;i++){
for(j=i+1;j<n;j++){
if(a[j]<a[i]){
temp=a[i];
a[i]=a[j];
a[j]=temp;
}//if
}//for
}
}
2.修改P1、P2、P3程序
Process1() 在阻塞前插入以下操作代码
Process1() 在阻塞前插入以下操作
Process1程序:
process1(){
char flag;
char a[100];
int len;
if(addr=='m') goto m;
i=1;m1=1;
a: printf("\n process1 printing m1=%d\n\n",m1);
printf("do you want to send data ?(y/n)"); //是否发送消息
flag=getchar();
if(flag=='y'||flag=='Y'){
printf("input data:");
scanf("%s",a);
len=strlen(a);
send(1,a,len); //将消息输送到缓冲队列中
printf("\n");
}
printf("process1 calls p on sem1!\n");
if(p(1,1,'m')==0)
return (0);
m: printf("\n=>process1 i=%d\n\n",i);
i=i+5;
goto a;
}
Process2程序:
process2(){
char flag;
char b[100];
int len;
if(addr=='m') goto m;
if(addr=='n') goto n;
i=1;
a: printf("\n process2 printing m1=%d\n",m1);
m2=2*m1;
printf("do you want to receive message?(y/n)");//是否接收消息
getchar();
flag=getchar();
if(flag=='y'||flag=='Y')
{
if(receive(1,b)==0){ //消息队列为空
getchar();
}else{ //打印接收的消息,并且排序
printf("\n%s\n\n",b);
len=strlen(b);
sort(b,len);
printf("\n%s\n\n",b);
send(2,b,len);}
}
printf("process2 call p on sem2\n");
if(p(2,2,'m')==0)
return (0);
m: printf("process2 call v on sem1!\n");
if(v(1,2,'n')==0)
return (0);
n: printf("\n=>process2 i=%d\n\n",i);
i=i+10;
goto a;
}
Process3()在P3调用V操作唤醒P2前加入以下操作
Process3程序:
process3(){
char c[100];
if(addr=='m') goto m;
if(addr=='n') goto n;
i=1;
a: printf("\n=>process3 i=%d\n",i);
if(i>=num) goto b;
printf("\n process3 printing 2^i=%d\n\n",m2);
m1=m2;
if(receive(2,c)){
printf("==>SORT RESULT:%s\n\n",c);
getchar();
}
else{
printf("==>SORT RESULT:\n\n");
getchar();
}
getchar();
printf("process3 call v on sem2!\n");
if(v(2,3,'m')==0) return (0);
m: i++;
goto a;
b: if(receive(2,c)){
printf("==>result:%s\n\n",c);
getchar();
}
else{
printf("==>result:\n\n");
getchar();
}
printf("\nprocess3 calls p on sem2\n");
if(p(2,3,'n')==0) return (0);
n: ;
}
四、测试情况、分析
下面的是调试正确后的程序结果:
(1)发送后立刻接受:
(2)接下来三个进程
五、收获与体会
经过这次的实验,我收获良多,同时也让我明白,动手了,实践了就有希望,就会有希望完成任务。
刚开始做这次实验的时候,发现很没有头绪,除了老师给的PPT就没有一点线索。只能硬着头皮去看之前做实验的程序代码,理解每个模块的作用,然后记录下来,对照之前老师给我们的讲解。我发现通过流程图编写程序是一个很好的方法,不仅仅调理清晰,最重要的是减少很多的参数,便于程序员很快的了解每个参数、模块的功能。
之后我们根据老师给的流程图写出了程序代码。
通过这个实验,我决定以后的编程过程中,我必定会写好每一个项目的流程,以最好的效率完成既定的任务。
第二篇:进程之间的同步互斥与通信理发师问题操作系统课程设计说明书(含源程程序)
操作系统课程设计
目 录
1. 课程设计目的............................................... 3
2. 设计要求.................................................. 3
3. 问题具体描述............................................... 3
4. 设计分析.................................................. 3
5. 设计分工.................................................. 4
6. 数据结构说明............................................... 4
7. 系统结构说明............................................... 4
8. 系统调用说明............................................... 4
9. 分工设计说明............................................... 5
10. 算法流程图................................................ 5
11. 分工代码................................................. 6
12. 整体代码................................................. 7
13. 程序运行................................................ 10
14. 总结.................................................... 11
1. 课程设计目的
1.内容围绕操作系统原理中最重要的基本概念和基本原理展开
2.巩固对原理知识的学习效果
3.加深对基本概念的理解
4.学习如何将基本原理和实际设计、应用有机结合
5.锻炼本专业的基本能力
2. 设计要求
1: 进程间通信、并发(同步/互斥)、文件读写
2: 内存管理、Dll、Windows消息机制、IO (尚未最终定型)
3. 问题具体描述
1.完成N个生产者和M个消费者之间的并发控制,N、M不低于5,数据发送和接收缓冲区 大小不小于10个。
2.某个生产者进程生产的消息供K个消费者进程消费。K《=M。某些消费进程消费多个生产者生产的消息。生产者和消费者之间的对应关系可以在程序开始有一个文件中读入,也可以动态调整。
3.每个生产进程生产M个消息后结束运行。如果一个消费者进程没有对应的生产者进程在运行后,也结束运行。
4. 设计分析
课程设计的主要目的是了解并且掌握进程之间的同步互斥,和进程之间的通信问题。结合课本上的生产者与消费者问题可以从这方面来实现一个多进程的小系统,并且解决多个进程之间的通信,并发等问题,以此来达到课程设计的目的。理发师问题是将顾客看做生产者,将理发师作为消费者。设置一定数量的椅子的数目来作为缓存区的大小。顾客来到的时候坐在椅子上,将自己作为“产品”,理发师理发的时候从椅子上叫走顾客,相当于消费“产品”,从而达到了课程设计要求的前一个要求。
顾客作为生产者,每到来一个就使计数器count增加1,以便让理发师理发(相当于消费)至最后一个顾客(相当于产品)。并且,第1个到来的顾客应负责唤醒理发师;如果不是第1个到达的顾客,则在有空椅子的情况下坐下等待,否则离开理发店(该消息可由计数器count获得)。主要有以下一些函数来实现整个问题的实现过程:
(1)用随机函数random()来产生进入理发店的顾客。
(2)定义理发师的理发函数cuthair()用来实现理发操作。
(3)定义顾客被理发的函数gethaircut()用来实现顾客被理发的操作。
(4)用顾客线程customer实现对顾客行为的控制。
(5)用理发师线程barber实现对理发师行为的控制。
(6)定义主函数main实现对两个线程的控制和执行操作。
5. 设计分工
成员:李宁 侯绍立
分工:
理发师进程、信号量的设置、理发师函数 :xxx
顾客进程、顾客函数、主函数:xx
6. 数据结构说明
本程序用到了数据结构中的队列,理发的顾客由随机函数产生,顾客遵从先到先理发的原则,但队列的长度限制为输入的理发店中的椅子的个数,当理发店的椅子没有空位的时候,到来的顾客主动退出加入队列。理发师对队列中的顾客进行先到先服务的原则理发。
7. 系统结构说明
(一)头文件声明
#include "windows.h"
#include "iostream.h"
#include "math.h"
(二)定义各种变量
int long waiting(0);
int chairs;
char open_door;
char close_door;
int count(0);
int finish(0);
(三)信号量的定义
HANDLE Mutex =::CreateMutex(NULL, FALSE, "Mutex");
HANDLE barbers =::CreateSemaphore(NULL, 1,1, "barbers");
HANDLE customers =::CreateSemaphore(NULL,0,3,"customers");
8. 系统调用说明
(1)CreateThread():创建线程
(2)CreateMutex():找出当前系统是否已经存在指定进程的实例。如果没有则创建一个互斥体,用来同步。如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。CreateMutex()函数可用来创建一个有名或无名的互斥量对象
(3)CreateSemaphore():CreateSemaphore() 是系统提供的API,包含在Windows.h 中,应用在同步的处理中。作用是创建一个新的信号机,执行成功,返回信号机对象的句柄;零表示出错。一旦不再需要,一定记住用CloseHandle关闭信号机的句柄。它的所有句柄都关闭以后,对象自己也会删除。一旦值大于零,信号机就会触发(发出信号)。Semaphore是一个同步问题机制,不论是Event或Mutex,其他Process在执WaitForSingleObject时,就看当时的物件是Signal或UnSignal而决定是否等待,而Semaphore也相同,但是它要变成Signal /UnSignal的状态,却有些不同,它是提供一个计数值,它允许在这个计数值之内,任何执行到WaitForSingleObject的Thread都不会停下来,而且每执行WaitForSingleObject一次,计数值就减一,当计数值变成0时,该Semaphore才会处於UnSignal的状态,而某个Thread ReleaseSemaphore时,便会将计数值增加,以便其他的Thread或本身可得Signal的讯号,而使WaitForSingleObject停止等待。
(4)ReleaseSemaphore():ReleaseSemaphore()函数的作用是增加信号机的计数。如果成功,就调用信号机上的一个等待函数来减少它的计数。
(5)WaitForSingleObject():函数用来检测hHandle事件的信号状态,在某一线程中调用该函数时,线程暂时挂起,如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果超时时间已经到达dwMilliseconds毫秒,但hHandle所指向的对象还没有变成有信号状态,函数照样返回。参数dwMilliseconds有两个具有特殊意义的值:0和INFINITE。若为0,则该函数立即返回;若为INFINITE,则线程一直被挂起,直到hHandle所指向的对象变为有信号状态时为止。
(6)ResumeThread():线程恢复函数,使用该函数能激活线程的运行,使CPU分配资源让线程恢复运行。
(7)ReleaseMutex():释放由线程拥有的一个互斥体
(8)Sleep():睡眠等待
9. 分工设计说明
需要用到的信号量:
HANDLE Mutex =::CreateMutex(NULL, FALSE, "Mutex");
实现顾客与理发师对公用缓存区的互斥操作
HANDLE barbers =::CreateSemaphore(NULL, 1,1, "barbers");
HANDLE customers =::CreateSemaphore(NULL,0,3,"customers");
::WaitForSingleObject(customers,INFINITE); 等待顾客进程的V(customer)操作
::WaitForSingleObject(Mutex,INFINITE); 申请操作顾客与理发师公用的缓存区
::ReleaseSemaphore(barbers,1,NULL); 释放信号量barbers
::ReleaseMutex(Mutex); 释放信号量Mutex,允许顾客操作缓存区
10. 算法流程图
11. 分工代码
#include "windows.h"
#include "iostream.h"
#include "math.h"
int long waiting(0); //等待理发的顾客人数
int chairs; //店中椅子的总数目
char open_door; //开门
char close_door; //关门
int count(0); //顾客的序号
int finish(0); //已经理完发的顾客人数
DWORD a;
HANDLE Mutex =::CreateMutex(NULL, FALSE, "Mutex"); //用来实现进程的互斥
HANDLE barbers =::CreateSemaphore(NULL, 1,1, "barbers"); //定义信号量来进行线程间的同步
HANDLE customers =::CreateSemaphore(NULL,0,3,"customers"); //定义信号量来进行线程间的同步
int random()
//定义随机函数来产生顾客,并使两个顾客间的时间少于15秒
{
return (rand()*15000)/RAND_MAX;
}
void cuthair()
//理发师的理发函数,用时15秒
{
::Sleep (15000);
cout<<"理发结束 !"<<endl;
}
DWORD WINAPI barber(LPVOID pParm1)
//理发师线程
{
while(true)
{
::WaitForSingleObject(customers,INFINITE);//p(customers),等待顾客
::WaitForSingleObject(Mutex,INFINITE); //等待互斥量
waiting--; //等待的人数减1
::ReleaseSemaphore(barbers,1,NULL); //释放信号量
::ResumeThread(barbers); //唤醒顾客进程
::ReleaseMutex(Mutex); //v(mutex);
cuthair();
finish++;
}
return 0;
}
12. 整体代码
#include "windows.h"
#include "iostream.h"
#include "math.h"
int long waiting(0); //等待理发的顾客人数
int chairs; //店中椅子的总数目
char open_door; //开门
char close_door; //关门
int count(0); //顾客的序号
int finish(0); //已经理完发的顾客人数
DWORD a;
HANDLE Mutex =::CreateMutex(NULL, FALSE, "Mutex"); //用来实现进程的互斥
HANDLE barbers =::CreateSemaphore(NULL, 1,1, "barbers"); //定义信号量来进行线程间的同步
HANDLE customers =::CreateSemaphore(NULL,0,3,"customers"); //定义信号量来进行线程间的同步
int random()
//定义随机函数来产生顾客,并使两个顾客间的时间少于15秒
{
return (rand()*15000)/RAND_MAX;
}
void cuthair()
//理发师的理发函数,用时15秒
{
::Sleep (15000);
cout<<"理发结束 !"<<endl;
}
void gethaircut()
// 顾客被理发的函数
{
::Sleep (15001); //顾客被理发的函数,为了和理发师之间有所区别,比理发师理发时间长1毫秒
cout<<"第"<<finish<<"个顾客理发完毕,离开 "<<endl;
}
DWORD WINAPI customer(LPVOID pParm2)
// 顾客线程
{
::WaitForSingleObject(Mutex ,INFINITE); //P(mutex)来进行互斥操作
count++; //来的是第几个顾客
cout<<"顾客敲门!第 "<<count<<" 个顾客到来 "<<endl;
if (waiting<chairs) //如果有空椅子
{
if (waiting!=0)
{
cout<<"现在有"<<waiting <<" 个人在等待理发"<<endl;
}
else
cout<<"无人在等待理发"<<endl; //输出有多少人在等待
waiting++;
cout<<"剩余"<<chairs-waiting+1<<"个座位"<<endl;
cout<<"有空位,顾客已坐下"<<endl;
::ReleaseSemaphore(customers,1,NULL); //V(customer)
::ResumeThread(customers); //唤醒理发师进程
::ReleaseMutex(Mutex); //释放互斥量,以便其他线程使用
::WaitForSingleObject(barbers,INFINITE);//等待理发
gethaircut();
}
else
{
cout<<"没有空椅子,第"<<count<<"个顾客离开理发店"<<endl; //没有椅子,顾客直接离开
::ReleaseMutex(Mutex);
}
return 0;
}
DWORD WINAPI barber(LPVOID pParm1)
//理发师线程
{
while(true)
{
::WaitForSingleObject(customers,INFINITE);//p(customers),等待顾客
::WaitForSingleObject(Mutex,INFINITE); //等待互斥量
waiting--; //等待的人数减1
::ReleaseSemaphore(barbers,1,NULL); //释放信号量
::ResumeThread(barbers); //唤醒顾客进程
::ReleaseMutex(Mutex); //v(mutex);
cuthair();
finish++;
}
return 0;
}
int main(int argc, char* argv[])
//实现线程的操作
{
cout<<"输入理发店中的椅子个数:";
cin>>chairs;
cout<<"店中有"<<chairs<<"把椅子"<<endl; //设置椅子数目
cout<<"是否开始接待顾客?Y/N"<<endl; //是否开门营业
cin>>open_door;
while (open_door!='y')
{
cout<<endl<<"尚未营业!"<<endl;
cout<<"是否开始接待顾客?Y/N"<<endl;
cin>>open_door;
}
HANDLE hThread1;
HANDLE hThread2;
hThread2=::CreateThread (NULL,0,barber,NULL,0,NULL); //产生一个理发师进程
while(close_door!='y')
{
::Sleep(random());//函数实现顾客随机到来
hThread1=::CreateThread(NULL,0,customer,NULL,a,NULL);
cout<<endl<<"正在营业,请进!"<<endl;
if (finish>=8 && waiting==0)
//如果完成数超过8并且没有人等待
{
cout<<"已经为"<<finish<<"个顾客理发了,是否停止营业?"<<endl; //提示是否关门
cin>>close_door;
return close_door;
}
}
if (close_door=='y')
{
cout<<"暂停营业!欢迎下次光临!"<<endl;
return 0;
}
return 0;
}
13. 程序运行
开始
输入椅子数目
选择是否接待顾客
接待顾客
理发完毕
14. 总结
通过这个课程设计,对于课本上的关于进程之间的同步、互斥有了更深层的认识,而且有了具体上直观上的理解。虽然对于第二个课程设计要求没有达到,但我也学到了许多。进程作为系统的基本的操作单位,其之间的互斥、同步是非常重要的。生产者与消费者这一个问题中,生产者与消费者公用一个缓存区,这就涉及到了两个进程对缓存区的互斥操作,否者将会产生许多不切实际的问题,这也就失去的系统操作的实际意义。而通过一个互斥信号量Mutex便很好的解决了这一个问题,两个进程在操作缓存区之前必须先申请使用,如果信号量表示已有进程正在对缓存区进行操作则必须等待其完成方可。在做这个设计的时候也遇到了许多问题,例如信号量的设置。由于这个问题中不仅涉及到理发师与顾客之间的互斥同步问题,还设计到各个顾客进程之间的互斥同步问题,所以起初信号量设置不足而导致问题出错。
总的来讲,此次课程设计对我们操作系统知识的巩固起了重要作用,让我们受到了很多启发,懂得了怎样更好的去学习操作系统课程。操作系统不在是一个模糊的概念存在,许多实际的例子,问题都能涉及到操作系统方面的知识。这也看出了操作系统对于整个计算机学科的重要性,他不单单作为一个学科存在,而是渗透到各个学科中,作为各个学科的基础甚至提升。虽然本次的设计不够完善,但我觉得自己获得的东西远远超过这个设计的内容。