苹果桔子问题的实现.docx
《苹果桔子问题的实现.docx》由会员分享,可在线阅读,更多相关《苹果桔子问题的实现.docx(26页珍藏版)》请在冰豆网上搜索。
苹果桔子问题的实现
摘要
本设计实际是生产者—消费者的变形,通过有界缓冲区把生产者和消费者联系起。
假定生产者和消费者的优先级是相同的,只要缓冲区未满,生产者就可以往缓冲区内放入产品。
苹果与橘子的问题是典型的进程同步问题。
本问题利用C语言实现相应的P、V原语。
主要过程可用生产消费者来模拟,这里,生产者(父亲和母亲)放入缓冲区(盘子)的产品有两类(苹果和桔子),消费者(女儿和儿子)也有两类,每类消费者只消费其中固定的一类产品。
生产者和消费者共享缓冲区,缓冲区中有空时,生产者可放入产品(不许放重),待缓冲区中有产品时,消费者可取出产品(不许取重),否则等待。
关键字:
进程同步;P、V操作;信号量
1.概述
1.1问题描述
桌上有一个空盘子,只允许放一个水果。
爸爸专向盘中放苹果,妈妈专向盘中放桔子,儿子专等吃盘中的桔子,女儿专等吃盘中的苹果。
规定当盘空时,一次只能放一个水果。
这个问题实际上是两个生产者和两个消费者被连接到仅能放一个产品的缓冲器上。
生产者各自生产不同的产品,但就其本质而言,他们是同一类生产者。
而消费者则各自去需要的产品消费,但消费的方式不同。
解决此类问题利用记录型信号量机制和P、V操作来实现进程同步。
进程同步是指一个进程的执行依赖于另一个进程的信号或消息,当一个进程没有得到来自与另一个进程的信号或消息时则等待,直到信号或消息到达才被唤醒。
1.2需求分析
本实验进行操作系统课设的主要任务是模拟生产者与消费者问题的一个衍生,即实现苹果--橘子问题。
这个题目有两个生产者,分别生产橘子和苹果。
有两个消费者,分别消费苹果和橘子。
同时,因为两个生产者和两个消费者同时对一个缓冲区进行操作。
所以应互斥的访问的缓冲区以保证程序的正确性。
本次进程的目的就是加深个进程之间的正确有效的访问一个存储单元缓冲区,即同步和互斥。
也要涉及到信号量在互斥访问中的使用,生产者和消费者问题的实现和流程问题。
当计算机中两个或者多个进程在执行时需要使用公用缓冲区,并且对该缓冲区采取了互斥措施,这时如果并发执行这些进程的时候就会造成CPU时间的极大浪费,这是操作系统设计要求不允许的。
而这种现象在操作系统和用户进程中大量存在。
因此为了解决这一问题,提出了同步的概念,即把异部环境下的一组并发进程,因直接制约而互相发送消息而进行互相合作、互相等待,使得各进程按一定的速度执行的过程称为进程间的同步。
1.3设计目的
该问题是典型的进程同步问题。
某些进程为了完成同一任务分工合作,由于合作的每一个进程都是独立的不可预知的推进,这就需要相互合作的进程在某些合作点上协调各自的工作。
当合作进程中的一个到达合作点后,在尚未得到其他合作进程发来的消息或信号前应阻塞自己,直到其合作进程发来协调信号或消息后才能被唤醒。
这就是进程同步要解决的问题。
借助C语言实现进程同步经典问题—苹果-桔子问题,用高级语言编写和调试一个进程同步程序,以加深对进程同步机制的理解。
通过用C语言模拟进程同步实现,加深理解有关进程同步机制的概念及P、V操作的应用。
通过该题目的设计过程,掌握进程同步问题的原理、软件开发方法并提高解决实际问题的能力。
1.4设计要求
这是进程同步与互斥问题的模拟,可以把向盘子放或取水果的每一个过程可以转为一个进程的操作,这些进程是互斥的,同时也存在一定的同步关系。
通过编程实践时,实际是随机的调用一个进程的操作,而这些进程的操作相当于程序中的函数调用。
而计算机在执行时每一个时刻只能执行一个操作,这就是互斥的表现。
同步的模拟可以类似于函数调用时的前提关系即先决条件。
这样进程同步模拟就完全可以通过函数的调用来实现。
为下面吃水果的问题创建进程并实现进程之间的同步模型。
能够处理以下的情形:
桌子上有一只盘子,最多可容纳两个水果,每次只能放入或者取出一个水果。
爸爸专门向盘子中放苹果,妈妈专门向盘子中放橘子,两个儿子专门等待吃盘子中的橘子,两个女儿专门等吃盘子中的苹果。
(1)分析设计要求,给出解决方案(说明设计所用的原理,采用的数据结构)。
(2)设计合适的测试用例,对得到的运行结果要有分析。
(3)设计中遇到的问题,设计的心得体会。
(4)设计到进程互斥与同步,更深了解操作系统。
2.概要设计
2.1数据结构
在此次程序中一共涉及到线程、函数、互斥信号、信号量和常变量。
在本程序中使用了Father、Mother、Son和Daughter等共4个线程。
Father、Mother线程的作用是模拟实现向盘子中放苹果或者橘子的操作,当主程序执行之后创建了Father或者Mother线程,则该线程会独自占用整个资源,并执行putapple或者putorange函数来实现操作,在执行Father或者Mother线程时,首先会将信号量empty减1,来说明盘子中已经放置了一个水果,之后将互斥信号mutex置0,来说明盘子已经被占用,其他进程无法占用。
当实现了putapple或者putorange后,该线程会置mutex为1,来释放资源,表明该资源可以被其他线程所使用。
接下来将apple或者orange信号进行相应的设置,表明在盘子中已经放置了苹果或者橘子,Daughter或者Son线程可以从盘子中取苹果或者橘子了。
Daughter、Son线程的作用是模拟实现从盘子中取出苹果或者橘子的操作,当主程序中执行过Father或者Mother线程之后会释放资源,这时候Daughter或者Son线程便得到了资源,在得到资源即盘子后会首先将apple或者orange信号量进行设置,恢复原来的数值以表明盘子中的水果情况,并且将mutex互斥信号进行设置,表示资源正在被占用。
在实现了getapple或者getorange后,便对mutex互斥信号进行设置,以释放资源。
有两类生产者,一类负责生产桔子,一类负责生产苹果:
有两类消费者,一类负责消费桔子,一类负责消费苹果;他们共享一个有储存单元的有界缓冲区,每个存储单元只能放入一种(桔子或苹果)。
二类生产者与二类消费者数目固定,即一个生产者负责生产桔子,一个个生产者负责生产苹果;一个个消费者负责消费桔子,一个消费者负责消费苹果。
二类生产者的生产速度与二类消费者额消费速度均可独立在程序界面调节,在运行中,该值调整后立即生效;多个生产者或多个消费者之间必须有共享对缓冲区进行操作的函数代码,同时需要考虑算法的效率性。
每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的全部内容、每当生产者与消费者的指针位置,以及生产者的消费者线程标识符。
(1)用一个整型变量plat_size表示盘子,初始值为0,plat_size=apple+orange,当放水果plat_size加1,取水果plat_size减1。
当plat_size为1时,表示盘子已满,此时若进行放水果操作,放水果将处于等待状态;当为0时,表示盘子已空,此时若进行取水果操作,取水果操作将处于等待状态。
(2)整型变量orang和apple分别表示盘中桔子和苹果的数目,初始值都为0.。
(3)用4个整型变量father_lag、mother_lag、son_lag、daughter_lag表示4个进程是否处于等待状态,处于等待状态时,变量值为1。
(4)两个水果同时处等待状态时,若有取水果的操作将自动执行等待放水果进程,执行按等待的先后顺序。
(5)用一个随机函数产生0~3的4个整数,分别对应4个进程调用。
intapple=0;//苹果个数
intorange=0;//桔子个数
intfather_lag=1;//father进程标志
intmother_lag=1;//mother进程标志
intson_lag=1;//son进程标志
intdaughter_lag=1;//女儿进程标志
intplat_size=0;//盘子中水果数量
intmf=0;//father与mother进程等待先后
2.2模块声明
本程序共创建了4个在windows系统环境下实现的线程,即Fahter、Mother、Son和Daughter等4个线程,以及putapple、putorange、getapple和getorange等4个函数,其中4个线程是用于实现爸爸、妈妈、儿子和女儿分别放水果和取水果的线程操作的,并分别调用这4个函数,来实现真正的操作。
在本程序中还设置了mutex互斥信号、empty、apple和orange等信号量,用于各线程之间获取资源和放弃资源等的线程之间的操作控制,并且设置了盘子容量上线常量content。
其主要功能是用于实现爸爸和妈妈这2个互斥线程对于资源的使用操作以及爸爸和女儿、妈妈和儿子这2组同步线程对于资源的操作。
若干个函数组成一个程序文件,若干个程序文件组成一个完整的程序,因此函数是程序的基本组成部分。
在本程序中共创建了putapple()、putorange()、getapple()和getorange()等4个功能函数,分别用来模拟实现放苹果、放橘子、取苹果和取橘子的操作。
主函数main()主要作用就是为Father、Mother、Son和Daughter这四个线程服务,创建这几个线程,并在这些线程执行完毕后销毁它们。
主函数中Sleep()函数,控制主线程休眠一段时间,并在此期间执行其他其他子线程,在休眠时间过后主线程执行完,整个程序执行完毕。
(1)主模块
用一个随机函数产生0~3的4个整数,分别对应4个进程的调用,调用的次数可以自己输入,本程序共产生10次随机调用过程。
(2)4个进程模块
爸爸向盘中放一个苹果操作:
father()
妈妈想盘中放一个桔子操作:
mother()
儿子从盘中取一个桔子操作:
son()
女儿从盘中取一个苹果操作:
daughter()
(3)printf()输出模块
用于输出盘中苹果和桔子的个数及有哪些进程处于等待状态。
(4)4个bool型的变量
Father_lag,Mother_lag,Son_lag,Daughter_lag,表示四个进程是否处于等待状态。
处于等待时,变量值为true。
3.详细设计
3.1界面设计
1、系统分析此次设计是实现特殊生产者和消费者的演示程序,所需要处理的信息是生产者和消费者的个数,生产苹果、橘子和消费苹果、橘子的速度控制,缓冲区中橘子和苹果的个数和当前生产、消费指针的位置。
2、程序中需要处理缓冲区的动态显示、生产者和消费者的速度可以调节,生产者和消费者个数可以改变。
为了实现界面的友好性,应该对用户标明清楚各个模块的作用。
同时实时的对程序进行暂停和停止。
演示程序中用图形显示的方法描述缓冲区的使用情况,即当前缓冲区有多少个苹果和橘子,还有生产和消费者的指针。
3、系统对外的界面如下:
可以调节橘子和苹果的生产速度和消费苹果和橘子的速度,在文本框中输入相应的速度,再按下修改按键即可实现速度的实时调节。
在苹果生产者、橘子生产者、苹果消费者、橘子消费者中实现对个数按钮的按下即可动态实时的调节生产者和消费者的个数的调节。
4、两个放水果进程同时处于等待状态时,若有取水果的操作将自动执行等待的放水果进程,执行按等待的先后顺序自动调用;取苹果或橘子进程同时处于等待状态,若有放苹果或橘子的操作将自动执行等待的取水果进程,进行按等待的先后顺序自动调用。
界面设计用于输出盘中苹果和桔子的个数及有哪些进程处于等待状态。
根据题目要求,该设计需要有四个进程;有一个公用存储单元缓冲区。
用4个整型变量father_lag、mother_lag、son_lag、daughter_lag表示4个进程是否处于等待状态,处于等待状态时,变量值为1。
3.2算法设计
在本系统中,爸爸与妈妈、儿子与女儿的线程操作是互斥的,但是爸爸与女儿、妈妈与儿子线程之间的操作是同步的。
因此要利用进程同步的方法来实现这几者之间的操作,当然其中也包含着互斥进程,因为盘子每次只能放入或者取出一个水果。
因为是在windows操作系统下实现的,所以是采用线程的方法实现的。
本程序算法使用if~else结构和for循环结构,该算法中有两类生产者。
一类负责生产橘子,一类负责生产苹果;有两类消费者,一类负责消费橘子,一类负责消费苹果。
他们共享存储单元的缓冲区。
每种存储单元仅能放入一种产品(苹果或橘子)。
二类生产者的生产速度与二类消费者的消费速度均可独立于程序界面上调节。
每个生产者和消费者对有界缓冲区进行操作之后,即时显示有界缓冲区的所有内容。
本程序只用到了互斥和信号量。
其中mutex是各线程之间的互斥信号,当一个线程占用了资源之后,其他线程在没接收到通知的时候即互斥信号为0时便无法使用资源。
empty、apple和orange为3个信号量,分别来表示资源的使用状态,各线程根据这3个信号量来了解资源状态和使用资源。
这3个信号量的变化范围为0~content,content为盘子的容量,也是资源的容量。
算法用P、V原语描述如下:
Semaphoreplat_size=1,
orange=0,
apple=0;
cobegin
父亲进程
do{
削一个苹果;
p(plat_size);
将苹果放入盘中;
v(apple);
}while
(1)
母亲进程
do{
剥一个桔子;
p(plat_size);
将桔子放入盘中;
v(orange);
}while
(1)
儿子进程
do{
p(oange);
从盘中去桔子;
v(plat_size);
吃桔子;
}while
(1)
女儿进程
do{
p(apple);
从盘中取苹果;
v(plat_size);
}while
(1)
3.3设计流程图
3.3.1爸爸放苹果进程操作流程图
爸爸放苹果进程的操作流程如图3.1:
plat_size表示盘子,初始值为0,plat_size=apple+orange,执行father操作后,plat_size=1;则daughter处于等待状态。
图3.1爸爸放苹果进程操作流程图
3.3.2妈妈放桔子进程操作流程图
妈妈放桔子进程的操作流程如图3.2:
plat_size表示盘子,初始值为0,plat_size=apple+orange,执行mother操作后,plat_size=1;则son处于等待状态。
如图所示,若mother放入前plat_size=1,则必须等待。
图3.2妈妈放桔子进程操作流程图
3.3.3儿子取桔子操作流程图
儿子取桔子的操作流程如图3.3:
son操作要进行,则必须plat_size=1,且盘子中必须有orange。
若orange==0,则son必须等待。
图3.3儿子取桔子操作流程图
3.3.4女儿取苹果操作流程图
女儿取苹果的操作流程如图3.4:
daughter操作要进行,则必须plat_size=1,且盘子中必须有apple。
若apple==0,则daughter必须等待。
Daghter进程调用中apple--;plat_size--;
图3.4女儿取苹果操作流程图
4.结果分析
初始运行状态如下:
运行程序后界面上显示“请输入调用次数”,根据调用的次数,系统会做出相应的调用。
初始设置了1次。
初始为daughter()调用。
此过程中初始化缓冲区(盘子)里有0个苹果,0个桔子。
father,mother,son进程均处于等待状态。
如图所示:
图4.1初始状态图
在第二次调用过程中,消费者daughter被调用。
调用之后存储单元的缓冲区中没有任何生产者生产的产品。
因此father,mother,son进程均处于等待状态。
当出现daughter()调用,son()调用时,缓冲区中没有可供消费的产品。
若缓冲区没有可供消费的产品,则消费者需等待。
如图所示:
图4.2father()调用
如图所示:
该操作中设置了八次调用。
下图是前四次的过程当执行father()操作时,缓冲区中有一个苹果。
而执行mother()操作时,缓冲区有一个桔子。
只有当缓冲区中有桔子或苹果时,才会有daughter()调用,son()调用,如下图第三次调用,若缓冲区有一个苹果,则daughter()操作自动被调用。
图4.3father()调用图
在本次程序运行过程中,由于第八次是father调用,则该进程中消费者儿子与消费者女儿互斥。
生产者放入存储缓冲区(盘子)的产品(苹果),被儿子取走后,另一位生产者才能生产(橘子)供女儿消费。
当操作完成后,按任意键退出该程序界面。
如图所示:
图4.4father()调用
5.设计总结
在此次操作系统课程设计中,我的题目是:
苹果—桔子问题的实现。
刚拿到这个任务时就感觉到了一种困难和挑战!
不知道从何下手开始设计程序,经过三天的思考,才有了一定的眉目。
最后在老师和同学的帮助下,终于得出了一套可行的方案。
依照策划的设计思想,又过了六天的编写和测试,终于实现了进程的同步功能,虽然整体还有待提高,但总算实现了基本功能,还算满意。
通过此次课程设计我对操作系统原理有了更进一步的了解,学会应用进程同步及P、V原语,相信会对以后的课程设计有很大的帮助作用。
也体会了到同学之间的相互合作帮助可以克服一切困难,尤其是在理论联系实际的过程中。
我的同学在设计过程中为我发现许多错误,也帮助我解决了很多问题,在此我衷心的感谢他们。
在以后的学习中我会更加注意各个方面的能力的协调发展,培养自己的动手能力和拓宽自己的知识面,逐渐提高自己的专业技能。
在课程设计时遇到了很多的问题,在老师的帮助,和对各种资料的查阅中,将问题一个一个的解决了,培养了我自主动手,独立研究的能力,为今后在学习工作中能更好的发展打下了坚实的基础。
总的来说这次试验比较成功,加深我对进程的理解,同时也提高了自己编程能力。
编程是个长久的过程,平时要多去动手实践,去提高自己的分析问题、发现问题、解决问题的能力。
参考文献
1.汤子瀛,哲凤屏.计算机操作系统.西安电子科技大学学出版社.
2.王清,李光明.计算机操作系统.冶金工业出版社.
3.孙钟秀等.操作系统教程.高等教育出版社.
4.曾明.操作系统应用教程.陕西科学技术出版社.
5.张丽芬,刘利雄.操作系统实验教程.清华大学出版社.
6.孟静,操作系统教程--原理和实例分析.高等教育出版社.
7.周长林,计算机操作系统教程.高等教育出版社.
8.张尧学,计算机操作系统教程.清华大学出版社.
9.任满杰,操作系统原理实用教程.电子工业出版社.
致谢
首先感谢我的老师和同学们在设计过程中给我提出了许多宝贵的意见和建议,并细心的帮助我解决问题,还在最后的调试程序的过程中帮我找出了一些潜在的错误,没有他们,我也许发现不了这些错误,在此非常感谢他们。
在这次课程设计的撰写过程中,我得到了许多人的帮助。
我要感谢我的老师在课程设计上给予我的指导、提供给我的支持和帮助,这是我能顺利完成这次报告的主要原因,更重要的是老师帮我解决了许多技术上的难题,让我能把系统做得更加完善。
在此期间,我不仅学到了许多新的知识,而且也开阔了视野,提高了自己的设计能力。
其次,我要感谢帮助过我的同学,他们也为我解决了不少我不太明白的设计商的难题。
同时也感谢学院为我提供良好的做毕业设计的环境。
最后再一次感谢所有在设计中曾经帮助过我的良师益友和同学。
附录
#include
#include
#include
intapple=0;
intorange=0;
intfather_lag=1;
intmother_lag=1;
intson_lag=1;
intdaughter_lag=1;
intplat_size=0;
intmf=0;
voidprint();
voidfather()//父进程
{
apple++;
print();
}
voidmother()//母进程
{
orange++;
print();
}
voidson()//儿子进程
{
orange--;
print();
}
voiddaughter()//女儿进程
{
apple--;
print();
}
voidprint()
{
printf("现在盘子里有%d个苹果,%d个桔子\n",apple,orange);
if(father_lag==1)
printf("father进程处于等待状态\n");
if(mother_lag==1)
printf("mother进程处于等待状态\n");
if(son_lag==1)
printf("son进程处于等待状态\n");
if(daughter_lag==1)
printf("daughter进程处于等待状态\n");
if((father_lag==0)&&(mother_lag==0)&&(son_lag==0)&&(daughter==0))
printf("\n");
}
voidmain()
{
intk;
inti;
intd;
printf("请输入调用次数:
\n");
scanf("%d",&d);
srand((unsigned)time(NULL));//随机产生一个以当前时间开始的随机种子
for(k=0;k{
printf("━━━━━━━━━━━━━━━━━━━━━\n");
printf("第%d次操作:
\n",k+1);
i=rand()%4;//随机生成1-5
plat_size=apple+orange;
switch(i)
{
case0:
printf("father调用.\n");
if(plat_size==1)
{
father_lag=1;//father处于等待状态
print();
if(mother_lag==0)
mf=1;//father与mother互斥信号量决定先后顺序
}
else{
father();
if(daughter_lag==1)
{
daughter_lag=0;//等待取消
printf("处于等待的daughter自动被调用\n");
daughter();
}
}
printf("━━━━━━━━━━━━━━━━━━━━\n");
break;
case1:
printf("mother调用.\n");
if(plat_size==1)
{
mother_lag=1;//mother处于等待状态
print();
if(father_lag==0)
mf=2;
}
else{
mother();
//print();
if(son_lag==1)//等待取消
{
son_lag=0;
printf("处于等待的son自动被调用。
\n");
son();
}
}
printf("━━━━━━━━━━━━━━━━━━━━\n");
break;
case2:
printf("son调用.\n");
if(orange==0)
{
son_lag=1;//son处于等待状态
print();
}
else{
son();
if(mother_