利用信号量机制解决哲学家进餐问题.docx
- 文档编号:7986139
- 上传时间:2023-01-27
- 格式:DOCX
- 页数:11
- 大小:767.01KB
利用信号量机制解决哲学家进餐问题.docx
《利用信号量机制解决哲学家进餐问题.docx》由会员分享,可在线阅读,更多相关《利用信号量机制解决哲学家进餐问题.docx(11页珍藏版)》请在冰豆网上搜索。
利用信号量机制解决哲学家进餐问题
成绩:
课程设计报告
课程名称:
课程设计(UNIX程序设计)
设计题目:
利用信号量机制解决哲学家进餐问题
姓名:
专业:
网络工程
班级:
学号:
计算机科学与技术学院
网络系
2013年12月28日
设计项目:
利用信号量机制解决哲学家进餐问题
一、选题背景
1965年,数学家迪杰斯特拉提出,并成为经典的IPC问题—哲学家进餐问题。
该问题的简单描述如下:
五个哲学家围坐在一张圆桌周围,每个哲学家面前都一盘通心粉。
由于通心粉很滑,需要两把叉子才能夹住。
哲学家的生活中有两种交替活动时段,吃饭(EATING)和思考(THINKING)。
当一个哲学家觉得饿了时,他就试图分两次去取左手边和右手边的叉子,每次拿一把,但不分次序。
如果成功拿到两把叉子,就进入吃饭状态,吃完后放下叉子继续思考。
二、设计思路
1.每个哲学家的行为活动状态为两种:
进餐(EATING)和思考(THINKING)。
因此创建一个有5个元素的状态数组,每个数组元素的状态值为EATING或者THINKING。
2.五个哲学家围坐成一圈,每两个人中间有一个叉子,即每个哲学家的边和右边有一个叉子,但这个叉子需要和旁边的邻居竞争使用。
对于每一个哲学家来说,其只有成功获得两个叉子,才能进入进餐状态。
在进完餐后,需要成功放下手中的两个叉子,才能进入思考的状态。
换个角度的描述就是,每个哲学家查询左右边的邻居当前状态,如果左右的邻居当前状态都为THINKING,则该哲学家可以进餐;如果左右邻居当前状态不都是THINKING,则哲学家不能进餐。
因此可以为每一个哲学家设置一个信号量,来描述哲学家的活动状态。
3.因为五只叉子做多只能允许两个哲学家进餐,所以可以将桌子作为一个临界资源。
通过设置一个互斥信号量来限制对临界资源的访问数。
4.创建两个动作函数,对应于每个哲学家的获取两把叉子和放下两把叉子的动作。
而每个动作都需要对互斥信号量和哲学家信号量进行访问操作,因此创建原子操作P和原子操作V,来执行对信号量的消耗和释放操作。
5.利用父进程创建五个子进程来模拟五个哲学家,在每个子进程中执行PHILOSOPHER(phi_num)函数来模拟每个哲学家进入哲学家进餐问题活动。
三、主要问题的解决方法和关键技术
1.共享状态数组问题。
问题描述:
因为状态数组是共享的,而每个模拟哲学家的子进程是相互独立的,有自己的地址空间,在进程之间共享使用状态数组出现问题。
解决方法:
父进程通过利用UNIX系统进程通信机制中共享内存机制的shmget()和shmat系统调用创建一个共享内存区,并将状态数组地址链接到进程地址空间,成功的解决了该问题。
2.信号量创建及初始化问题
问题描述:
整个程序使用两个不同的信号量,一个是记录型信号量数组,一个是互斥信号量,并且在信号量创建初就需要对信号量进行初始化,这样才能保证接下来的子进程运行时,五个子进程面对的是相同值的信号量数组。
解决方法:
父进程通过利用UNIX系统进程通信机制中信号量机制的semget()系统调用和semctl()系统调用,成功创建一个五元素的信号量数组和一个元素的信号量数组,并将其在初始为设计的初始值,保证了程序后续操作的正确。
3.P和V原子操作问题
问题描述:
在子进程中的对信号量的操作必须是原子操作P和V,而且由于在动作函数中需要调用P和V原子操作,所以必须保证P和V操作的原子性,否则函数之间参数的传递将出现不一致。
解决方法:
利用UNIX系统进程通信机制中信号量机制的semop()系统调用,封装P和V操作函数,保证了函数的原子性。
4.进程创建的准确时刻问题
问题描述:
根据设计的描述,程序是通过五个子进程来模拟五个哲学家,但对进程创建的先后顺序没有要求,又由于进程分配到时间片是有限的,并且在创建的过程中要保证五个子进程都是统一父进程创建的,所以对于进程的创建时刻不太容易确定。
解决方法:
利用的UNIX系统的进程创建fork()系统调用,创建一个有五个子进程的进程扇,成功的解决创建准确时刻问题
5.哲学家动作函数编译问题
问题描述:
在哲学家的三个动作函数中需要对两个类型的信号量进行操作,所以不可避免的需要调用P和V原子操作。
尽管在动作函数定义之前,已经声明了P和V函数。
但是由于其原子性,在动作函数中直接使用P,V函数导致了严重的编译错误问题。
解决方法:
通过在各个函数参数列表中添加需要指向将使用的P和V函数指针,并在使用时采用(*P)和(*V)形式来解除引用操作,成功的解决了编译错误问题。
四、程序流程图
约定:
Take_forks函数动作TKFK
Put_forks函数动作PTFK
Test函数动作TEST
THINKING思考状态THINK
EATING进餐状态EAT
N
Y
N
Y
YN
五、原程序清单
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#defineN5
#defineLEFT(i)(i+N-1)%N
#defineRIGHT(i)(i+1)%N
#defineTHINKING0
#defineEATING1
//#include"behavior_philosophy.h"
voidphilosopher(int,char*,int,int,void(*)(int,int),void(*)(int,int));
voidtake_forks(int,char*,int,int,void(*)(int,int),void(*)(int,int));
voidput_forks(int,char*,int,int,void(*)(int,int),void(*)(int,int));
voidtest(int,char*,int,void(*)(int,int));
voidthink(int);
voideat(int);
voidP(int,int);
voidV(int,int);
/*------------------------哲学家动作-----------------*/
voidphilosopher(inti,char*state,intsem_phiid,intsem_mutexid,void(*P)(int,int),void(*V)(int,int)){
sleep
(1);
think(i);
while
(1){
take_forks(i,state,sem_phiid,sem_mutexid,P,V);
eat(i);
put_forks(i,state,sem_phiid,sem_mutexid,P,V);
think(i);
}
}
voidtake_forks(inti,char*state,intsem_phiid,intsem_mutexid,void(*P)(int,int),void(*V)(int,int)){
(*P)(sem_mutexid,0);
state[i]=THINKING;
test(i,state,sem_phiid,V);
(*V)(sem_mutexid,0);
(*P)(sem_phiid,i);
}
voidput_forks(inti,char*state,intsem_phiid,intsem_mutexid,void(*P)(int,int),void(*V)(int,int)){
(*P)(sem_mutexid,0);
state[i]=THINKING;
test(LEFT(i),state,sem_phiid,V);
test(RIGHT(i),state,sem_phiid,V);
(*V)(sem_mutexid,0);
}
voidtest(inti,char*state,intsem_phiid,void(*V)(int,int)){
if(state[i]==THINKING&&state[LEFT(i)]!
=EATING&&state[RIGHT(i)]!
=EATING){
state[i]=EATING;
(*V)(sem_phiid,i);
}
}
voidthink(inti){
printf("philosopher:
%d>>>>>isTHINKING.\n",i);
}
voideat(inti){
printf("I'mphilosopher:
%d>>>>>isEATING.andwilleating%dseconds!
\n",i,sleep
(2));
}
/*---------------------P,V原子操作------------*/
voidP(intsemid,intindex){
structsembufsema_buffer;
sema_buffer.sem_num=index;
sema_buffer.sem_op=-1;
sema_buffer.sem_flg=SEM_UNDO;
semop(semid,&sema_buffer,1);
}
voidV(intsemid,intindex){
structsembufsema_buf;
sema_buf.sem_num=index;
sema_buf.sem_op=1;
sema_buf.sem_flg=SEM_UNDO;
semop(semid,&sema_buf,1);
}
/*-----------------------------------------------------------*/
intmain()
{
/*-------------------------创建信号量操作----------------*/
intsem_phiid;
sem_phiid=semget(1008,5,0666|IPC_CREAT);
assert(sem_phiid>=0);
unsignedshortarray[5]={0,0,0,0,0};
unionsemun{
intval;
unsignedshort*array;
}semopts;
semopts.array=array;
intret1=semctl(sem_phiid,0,SETALL,semopts);
assert(ret1==0);
intsem_mutexid;
sem_mutexid=semget(0x225,1,0666|IPC_CREAT);
assert(sem_mutexid>=0);
semopts.val=1;
intret2=semctl(sem_mutexid,0,SETVAL,semopts);
assert(ret2==0);
/*------------------初始化共享内存-----------------------*/
intshmid;
char*state;
if((shmid=shmget(IPC_PRIVATE,N,0600|IPC_CREAT))==-1){
semctl(sem_phiid,0,IPC_RMID,0);
semctl(sem_mutexid,0,IPC_RMID,0);
perror("shmgetfaild!
");
exit
(1);
}
if((state=shmat(shmid,0,0))==(char*)-1){
semctl(sem_phiid,0,IPC_RMID,0);
semctl(sem_mutexid,0,IPC_RMID,0);
perror("shmatfaild!
");
exit
(1);
}
/*-----------------------------------------------*/
inti,phinum;
pid_tpid;
for(i=0;i<5;i++){
while((pid=fork())==-1);
if(!
pid){
phinum=i;
signal(SIGINT,SIG_DFL);
break;
}
}
if(pid>0){
while(wait((int*)0)==-1);
semctl(sem_phiid,0,IPC_RMID,0);
semctl(sem_mutexid,0,IPC_RMID,0);
shmdt(state);
printf("Hi,GAMEOVER!
");
}
else
philosopher(phinum,state,sem_phiid,sem_mutexid,P,V);
}
六、程序运行结果
截图一
截图二
截图三
七、设计总结
这次课程设计通过利用UNIX系统的信号量机制和共享内存机制,编写一个解决哲学家进餐问题的程序,感觉有很多收获,主要体现在以下几点:
(1)很好的复习了UNIX系统编程课上所学的知识,尤其是对unix进程通信机制中信号量机制和共享内存机制有了实践和更深的认识。
(2)练习了C编程,尤其是对函数指针有了更深的认识。
(3)复习了操作系统进程通信的原理。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 利用 信号量 机制 解决 哲学家 进餐 问题