利用信号量机制解决哲学家进餐问题
- 格式:doc
- 大小:1003.00 KB
- 文档页数:12
利用信号量机制解决哲学家进餐的问题c语言哲学家进餐问题是经典的并发编程问题,主要是模拟多个哲学家同时在桌子上进行吃饭和思考。
由于每个哲学家都需要同时拿起左右两边的餐叉才能进餐,如果不加以控制,容易导致死锁的情况发生。
为了解决这个问题,可以利用信号量机制来确保每个哲学家同时只能拿到一把餐叉,从而避免死锁的发生。
首先,我们需要定义一些全局变量和信号量来表示哲学家和餐叉的状态。
```c#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <semaphore.h>#define N 5 // 哲学家的数量pthread_t philosophers[N]; // 哲学家的线程pthread_mutex_t forks[N]; // 餐叉的互斥锁sem_t room; // 哲学家就餐的信号量void *philosopher(void *arg) {int id = *(int *)arg;int right = id;int left = (id + 1) % N;while (1) {// 思考sleep(rand() % 3 + 1);// 请求餐叉sem_wait(&room);pthread_mutex_lock(&forks[right]);pthread_mutex_lock(&forks[left]);// 进餐sleep(rand() % 3 + 1);printf("Philosopher %d is eating\n", id + 1);// 放下餐叉pthread_mutex_unlock(&forks[left]);pthread_mutex_unlock(&forks[right]);sem_post(&room);}}int main() {int i, id[N];// 初始化互斥锁和信号量sem_init(&room, 0, N - 1); // 初始信号量为N-1,表示有N-1个座位可供就餐for (i = 0; i < N; i++) {pthread_mutex_init(&forks[i], NULL);}// 创建哲学家线程for (i = 0; i < N; i++) {id[i] = i;pthread_create(&philosophers[i], NULL, philosopher, &id[i]); }// 等待哲学家线程结束for (i = 0; i < N; i++) {pthread_join(philosophers[i], NULL);}// 销毁互斥锁和信号量for (i = 0; i < N; i++) {pthread_mutex_destroy(&forks[i]);}sem_destroy(&room);return 0;}```在主函数中,我们首先定义了哲学家和餐叉的线程以及信号量。
红河学院课程设计报告操作系统课程名称:6个哲学家进餐设计题目:院系:工学院专业:计算机科学与技术班级:11计科班曹永前设计者:学号:201101030466指导教师:韦相2013 年 5 月26 日1. 问题描述:一个房间内有6个哲学家,他们的生活就是思考和进食。
哲学家思考后,过一定的时间就会饥饿,饥饿之后就想吃饭,吃饭后再思考。
房间里有一张圆桌,桌子周围放有五把椅子,分别属于五位哲学家每两位哲学家之间有一把叉子,哲学家进食时必须同时使用左右两把叉子。
2. 问题分析1、写出哲学家进餐的算法描述。
用六只筷子解决需要用两双筷子来进餐的六个哲学家,由于每个哲学家都需要其周围的两只筷子,所以筷子是公用信号量,这久需要设置一个互斥信号量,来使六个哲学家互斥的进餐.具体做法将六个信号量设置为0-5,用pv 源于来控制信号量,并将六个哲学家分别编号为0-5.经过仔细分析我们会发现,有这样一个问题存在,就是当每个哲学家都申请到他周围的一只筷子时,由于他们每人都只有一只筷子无法进餐,没有进餐他们就无法释放他们已经得得到的筷子,这样是进餐出于一种僵局,无法继续下去,这就是死锁问题.2、死锁问题的分析与具体的解决方法。
死锁问题就是当每个哲学家都拿到且只拿到一只筷子,这样每个哲学家都无法进餐,也无法释放所得到的筷子,所以解决死锁我们就要从这入手,就是怎样去预防使所有哲学家不要同时去申请他们同一方向的筷子.根据这解决死锁的方法有以下几种:a.每一次最多只能有五个哲学家申请进餐.这样其中的一个哲学家就能申请到两只筷子,就能够进餐,再将筷子释放给其他哲学家进餐.b.用AND信号量,就是哲学家需同时申请其左右两边的筷子,两边都有资源的时候,才能让这个哲学家得到资源,这样哲学家只要申请到筷子就能进餐, 再将筷子释放给其他哲学家进餐.c.用管程机制来实现。
d.我们前面已经将每个哲学家都分配了一个编号,我们可以编号为奇数的哲学家首先去申请其左边的筷子,再去申请其右手边的筷子;让编号为偶数的哲学家,先去申请其右边的筷子,再去申请其左边的筷子.我们可以看出编号为奇数的哲学家左边,与编号为偶数的哲学家的右边为同一只筷子,当其中一个哲学家拿到此筷子后,他另一边的筷子也是空闲的,这样就能避免死锁.主程序中我使用的是最后一种避免死锁的方法.3、用C程序实现哲学家进餐。
利用信号量机制解决哲学家进餐的问题c语言
利用信号量机制解决哲学家进餐问题是 C 语言中一个经典的同
步问题。
在该问题中,有五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,他们在圆桌上有五个碗和五只筷子,每个哲学家轮流思考和进餐。
为了协调五个哲学家的进餐,需要利用信号量机制来控制同时可拿起筷子的最大人数。
具体实现步骤如下:
1. 初始化信号量数组 semchopsticks 和 semeaters,分别模拟五根筷子和同时可拿起筷子的最大人数。
2. 创建五个线程,每个线程负责控制一个哲学家的进餐。
每个
线程包含以下步骤:
a. 等待信号量 semeaters,表示当前哲学家正在思考。
b. 等待信号量 semchopsticks 与自己编号相同的筷子的信号量,表示该哲学家需要拿起筷子。
c. 拿起编号与自己相同的筷子,并等待信号量 semchopsticks 与自己编号不同的筷子的信号量,表示该哲学家已经进餐完毕。
d. 释放信号量 semeaters 和 semchopsticks。
3. 每个线程结束时,释放信号量 semeaters 和 semchopsticks,以便其他线程进入执行状态。
4. 在主函数中,创建多个线程并启动它们,最后等待所有线程
执行完毕。
linuxc语⾔哲学家进餐---信号量PV⽅法⼀1、实验原理 由Dijkstra提出并解决的哲学家进餐问题(The Dinning Philosophers Problem)是典型的同步问题。
该问题是描述有五个哲学家共⽤⼀张圆桌,分别坐在周围的五张椅⼦上,在圆桌上有五个碗和五只筷⼦,他们的⽣活⽅式是交替地进⾏思考和进餐。
平时,⼀个哲学家进⾏思考,饥饿时便试图取⽤其左右最靠近他的筷⼦,只有在他拿到两只筷⼦时才能进餐。
进餐完毕,放下筷⼦继续思考。
2.实验内容:显⽰出每个哲学家的⼯作状态,如吃饭,思考。
连续运⾏30次以上都未出现死锁现象。
3.分析解决⽅案⼀:现在引⼊问题的关键:这些哲学家很穷,只买得起五根筷⼦。
他们坐成⼀圈,两个⼈的中间放⼀根筷⼦。
哲学家吃饭的时候必须同时得到左⼿边和右⼿边的筷⼦。
如果他⾝边的任何⼀位正在使⽤筷⼦,那他只有等着。
所以我们就假设最多只有4民哲学家在进餐这样就能保证没有死锁的情况。
代码如下:1 #include<unistd.h>2#define NUM 53int ID[NUM]={0,1,2,3,4};4 sem_t sem_chopsticks[NUM];5 sem_t sem_eaters;6int eaters_num=0;//记录已经吃过饭的次数78//初始化信号量函数9void sem_signal_init(){10int i;11for(i=0;i<NUM;i++){12if(sem_init(&sem_chopsticks[i],0,1)==-1){13 perror("oops:em_init error!");14 exit(1);15 }16 }17if(sem_init(&sem_eaters,0,NUM-1)==-1){18 perror("oops:em_init error!");19 exit(1);20 }212223 }24252627//执⾏函数,每个线程相当于⼀个哲学家来进⾏28void * philosopher(void *ptid){29int pthread_id=*(int *)ptid%NUM;30 printf("%d philosopher is thinking...\n",(int)pthread_id);31 sem_wait(&sem_eaters);32//申请左筷⼦33 sem_wait(&sem_chopsticks[pthread_id]);34 printf("%d philosopher takes chopstick %d...\n",(int)pthread_id,(int)pthread_id);35//申请右筷⼦36 sem_wait(&sem_chopsticks[(pthread_id+1)%NUM]);37 printf("%d philosopher takes chopstick %d...\n",(int)pthread_id,((int)pthread_id+1)%NUM);38 printf("%d philosopher is eating, %d philosopher had already dined.\n",(int)pthread_id,eaters_num);39 sem_post(&sem_chopsticks[(pthread_id+1)%NUM]) ;40 sem_post(&sem_chopsticks[pthread_id]);41 sem_post(&sem_eaters);42 eaters_num++;//吃过⼀次的⼈加加43 printf("%d philosopher had dined, by now %d philosopher had already dined.\n",(int)pthread_id,eaters_num); 4445 }4647484950int main(int argc,char *argv[]){51int i,l,j,k;52//循环五个线程多少次53for(l=0;l<1;++l){54 printf("*******%d times try *******",l+1);55 pthread_t philosopher_threads[NUM];56 sem_signal_init();57//循环创建五个线程并执⾏58for(i=0;i<NUM;i++){5960 printf("%d times\n",i);61if(pthread_create(&philosopher_threads[i],NULL,philosopher,&ID[i])!=0){62 perror("oops:pthread_create error!");63 exit(1);6465 }666768 }6970//⽗线程等待⼦线程都结束才继续执⾏71for(j=0;j<NUM;j++){72 pthread_join(philosopher_threads[j],NULL);73 }74//结束销毁信号量75 sem_destroy(&sem_eaters);76for(k=0;k<NUM;++k){77 sem_destroy(&sem_chopsticks[k]);78 }79 eaters_num=0;80 sleep(2);818283 }84858687return0;88 }运⾏结果如下:1 philosopher is thinking...0 philosopher takes chopstick 0...3 times1 philosopher takes chopstick 1...2 philosopher is thinking...4 times1 philosopher takes chopstick 2...1 philosopher is eating, 0 philosopher had already dined.3 philosopher is thinking...4 philosopher is thinking...1 philosopher had dined, by now 1 philosopher had already dined.2 philosopher takes chopstick 2...0 philosopher takes chopstick 1...0 philosopher is eating, 1 philosopher had already dined.4 philosopher takes chopstick 4...3 philosopher takes chopstick 3...0 philosopher had dined, by now 2 philosopher had already dined.4 philosopher takes chopstick 0...4 philosopher is eating, 2 philosopher had already dined.4 philosopher had dined, by now 3 philosopher had already dined.3 philosopher takes chopstick 4...3 philosopher is eating, 3 philosopher had already dined.3 philosopher had dined, by now4 philosopher had already dined.2 philosopher takes chopstick 3...2 philosopher is eating, 4 philosopher had already dined.2 philosopher had dined, by now 5 philosopher had already dined.--------------------------------Process exited after 2.127 seconds with return value 0请按任意键继续. . .后续⽅案将继续更新请关注。
哲学家进餐问题-3中解决⽅案问题描述⼀张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆⼀根筷⼦,桌⼦的中间是⼀碗⽶饭,如图2-10所⽰。
哲学家们倾注毕⽣精⼒⽤于思考和进餐,哲学家在思考时,并不影响他⼈。
只有当哲学家饥饿的时候,才试图拿起左、右两根筷⼦(⼀根⼀根地拿起)。
如果筷⼦已在他⼈⼿上,则需等待。
饥饿的哲学家只有同时拿到了两根筷⼦才可以开始进餐,当进餐完毕后,放下筷⼦继续思考。
问题分析1) 关系分析。
5名哲学家与左右邻居对其中间筷⼦的访问是互斥关系。
2) 整理思路。
显然这⾥有五个进程。
本题的关键是如何让⼀个哲学家拿到左右两个筷⼦⽽不造成死锁或者饥饿现象。
那么解决⽅法有两个,⼀个是让他们同时拿两个筷⼦;⼆是对每个哲学家的动作制定规则,避免饥饿或者死锁现象的发⽣。
⼀共5个哲学家,编号0 ~4, 5⽀筷⼦编号也是0 ~4, 0号哲学家右⼿的筷⼦编号是0号,逆时针增加,哲学家的编号也是逆时针增加所以:0号哲学家对应的是: 4号和1号筷⼦.1号哲学家对应的是: 0号和2号筷⼦.2号哲学家对应的是: 1号和3号筷⼦.3号哲学家对应的是: 2号和4号筷⼦.4号哲学家对应的是: 3号和0号筷⼦.所以有宏定义:#define left(phi_id) (phi_id+N-1)%N#define right(phi_id) (phi_id+1)%NN = 55⽀筷⼦对应5个互斥锁,所以:pthread_mutex_t forks[N]={PTHREAD_MUTEX_INITIALIZER};哲学家线程需要执⾏的动作是:void take_forks(int id){//获取左右两边的筷⼦printf("Pil[%d], left[%d], right[%d]\n", id, left(id), right(id));pthread_mutex_lock(&forks[left(id)]);pthread_mutex_lock(&forks[right(id)]);//printf("philosopher[%d] take_forks...\n", id);}void put_down_forks(int id){printf("philosopher[%d] is put_down_forks...\n", id);pthread_mutex_unlock(&forks[left(id)]);pthread_mutex_unlock(&forks[right(id)]);}void* philosopher_work(void *arg){int id = *(int*)arg;printf("philosopher init [%d] \n", id);while(1){thinking(id);take_forks(id);eating(id);put_down_forks(id);}}该算法存在以下问题:当五个哲学家都想要进餐,分别拿起他们左边筷⼦的时候(都恰好执⾏完pthread_mutex_unlock(&forks[left(id)]);)筷⼦已经被拿光了,等到他们再想拿右边的筷⼦的时候(执⾏pthread_mutex_unlock(&forks[right(id)]);)就全被阻塞了,这就出现了死锁。
include <>include <>include <string>include <iostream>include <>using namespace std;bool tools5; //全局变量,用餐工具CRITICAL_SECTION cs; //信号量, 在线程中使用,临界区class Philosopher{private:int number;int status; /标记当前哲学家的状态,0表示正在等待即处于饥饿状态,1表示得到两支筷子正在吃饭,2表示正在思考/ public:Philosopherint num=0: status2, numbernum { }const int find{return number;}const int getinfo{ return status; }void Change ; //状态改变函数void dead_lock;};/////////void Philosopher::dead_lock{EnterCriticalSection &cs ; //进入临界区 string s;ifstatus==1{toolsnumber%5=true;// toolsnumber-1%5=true;status=2;}else ifstatus==2{status=0;//toolsnumber-1%5=false;//toolsnumber-1%5=true;}else ifstatus==0{toolsnumber%5=false;toolsnumber-1%5=false;status=1;}LeaveCriticalSection &cs ;// cout<<"";}/////////void Philosopher::Change{EnterCriticalSection &cs ; //进入临界区 ifstatus==1 //正在进餐{toolsnumber%5=true; //放下左手工具 toolsnumber-1%5=true; //放下右手工具 status=2; //改变状态为思考}else ifstatus==2 //思考中{status=0; //改变状态为等待}else ifstatus==0 //等待中{iftoolsnumber%5&&toolsnumber-1%5 //左右手两边工具均为空闲状态{toolsnumber%5=false; //拿起左手工具toolsnumber-1%5=false; //拿起右手工具status=1;}}LeaveCriticalSection &cs ;}string printPhilosopher pA{//pA->Change;int i=pA->getinfo;string str;ifi==0str="等待";else ifi==1str="就餐";else str="思考";return str;}string toolstatusbool a{string state;ifa==truestate="闲";ifa==falsestate="用";return state;}int main{char con='y'; //判断是否继续// con = 'n';forint i=0;i<5;i++toolsi=true; //筷子都未使用,初始化Philosopher P11,P22,P33,P44,P55;InitializeCriticalSection &cs ; //初始化初始化临界区cout<<"-----------------------状态说明示意图:-----------------------"<<endl;cout<<" "<<"哲学家1号的状态"<<" "<<endl;cout<<" 筷子0的状态"<<" "<<"筷子1的状态"<<endl;cout<<"哲学家5号的状态"<<" "<<"哲学家2号的状态"<<endl;cout<<" 筷子4的状态"<<" "<<"筷子2的状态"<<endl;cout<<" 哲学家4号的状态"<<" "<<"哲学家3号的状态"<<endl;cout<<" "<<"筷子3的状态"<<endl;//cout<<" "<<"哲学家3号的状态"<<" "<<endl;cout<<"筷子的状态,用表示使用中,闲表示空闲中;"<<endl;cout<<"--------------------------------------------------------------"<<endl ;//cout<<"哲学家们开始生活:"<<endl;//cout<<"当前状态:";cout<<endl;//cin>>con;whilecon=='y'{; ; ; ; ;cout<<"当前状态为:"<<endl;cout<<" "<<<<print&P1<<" "<<endl;cout<<" "<<toolstatustools0<<" "<<toolstatustools1<<endl;cout<<" "<<<<print&P5<<" "<<<<print&P2<<endl;cout<<" "<<toolstatustools4<<" "<<toolstatustools2<<endl;cout<<" "<<<<print&P4<<" "<<<<print&P3<<endl;cout<<" "<<toolstatustools3<<endl;cout<<"--------------------------"<<endl;cout<<"若要继续下一状态,输入y;输入n进入死锁;输入其他,结束程序:";cin>>con;Sleep20;}whilecon=='n'{;; ; ; ;cout<<"死锁情况"<<endl;cout<<" "<<<<print&P1<<" "<<endl;cout<<" "<<toolstatustools0<<" "<<toolstatustools1<<endl;cout<<" "<<<<print&P5<<" "<<<<print&P2<<endl;cout<<" "<<toolstatustools4<<" "<<toolstatustools2<<endl;cout<<" "<<<<print&P4<<" "<<<<print&P3<<endl;cout<<" "<<toolstatustools3<<endl;cout<<"--------------------------"<<endl;cout<<"输入n继续;输入其他,结束程序:";cin>>con;Sleep20;}DeleteCriticalSection &cs ; //退出资源区return 0;}。
利⽤AND信号量机制解决哲学家进餐问题哲学家就餐问题是1965年由Dijkstra提出的⼀种线程同步的问题。
问题描述:⼀圆桌前坐着5位哲学家,两个⼈中间有⼀只筷⼦,桌⼦中央有⾯条。
哲学家思考问题,当饿了的时候拿起左右两只筷⼦吃饭,必须拿到两只筷⼦才能吃饭。
上述问题会产⽣死锁的情况,当5个哲学家都拿起⾃⼰右⼿边的筷⼦,准备拿左⼿边的筷⼦时产⽣死锁现象。
解决办法: 1、添加⼀个服务⽣,只有当经过服务⽣同意之后才能拿筷⼦,服务⽣负责避免死锁发⽣。
2、每个哲学家必须确定⾃⼰左右⼿的筷⼦都可⽤的时候,才能同时拿起两只筷⼦进餐,吃完之后同时放下两只筷⼦。
3、规定每个哲学家拿筷⼦时必须拿序号⼩的那只,这样最后⼀位未拿到筷⼦的哲学家只剩下序号⼤的那只筷⼦,不能拿起,剩下的这只筷⼦就可以被其他哲学家使⽤,避免了死锁。
这种情况不能很好的利⽤资源。
package操作系统;class Philosopher extends Thread {private String name;private Fork fork;public Philosopher(String name, Fork fork) {super(); = name;this.fork = fork;}@Overridepublic void run() {thinking();fork.takeFork();eating();fork.putFork();}void eating() {System.out.println("I'm eating " + name);try {sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}void thinking() {System.out.println("I'm thinking" + name);try {sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}class Fork {boolean[] used = { false, false, false, false, false };synchronized void takeFork() {String threadName = Thread.currentThread().getName();String name = threadName.substring(threadName.length()-1, threadName.length());int i = Integer.parseInt(name);while (used[i] || used[(i + 1) % 5]) {try {wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}used[i] = true;used[(i + 1) % 5] = true;}synchronized void putFork() {String threadName = Thread.currentThread().getName();String name = threadName.substring(threadName.length()-1, threadName.length());int i = Integer.parseInt(name);used[i] = false;used[(i + 1) % 5] = false;notifyAll();}}public class哲学家就餐问题 {public static void main(String[] args) {Fork fork = new Fork();new Philosopher("1", fork).start();new Philosopher("2", fork).start();;new Philosopher("3", fork).start();;new Philosopher("4", fork).start();;new Philosopher("5", fork).start();;}}执⾏结果I'm thinking1I'm thinking3I'm thinking2I'm thinking4I'm thinking5I'm eating 1I'm eating 3I'm eating 5I'm eating 2I'm eating 4。
哲学家就餐问题实验⼀⼀、实验名称:哲学家就餐问题的实现⼆、实验学时:2三、实验内容和⽬的:实验⽬的:实现哲学家就餐问题,要求不能出现死锁。
通过本实验熟悉Linux系统的基本环境,了解Linux下进程和线程的实现。
实验内容:在Unix系统下实现教材2.4.2节中所描述的哲学家就餐问题。
要求显⽰出每个哲学家的⼯作状态,如吃饭,思考。
连续运⾏30次以上都未出现死锁现象。
四、实验原理:由Dijkstra提出并解决的哲学家进餐问题(The Dinning Philosophers Problem)是典型的同步问题。
该问题是描述有五个哲学家共⽤⼀张圆桌,分别坐在周围的五张椅⼦上,在圆桌上有五个碗和五只筷⼦,他们的⽣活⽅式是交替地进⾏思考和进餐。
平时,⼀个哲学家进⾏思考,饥饿时便试图取⽤其左右最靠近他的筷⼦,只有在他拿到两只筷⼦时才能进餐。
进餐完毕,放下筷⼦继续思考。
五、实验器材(设备、元器件)(1)学⽣每⼈⼀台PC,安装WindowsXP/2000操作系统。
(2)局域⽹络环境。
(3)个⼈PC安装VMware虚拟机和Ubuntu系统。
六、实验内容:(⼀)熟悉Ubuntu系统下的多线程编程。
(⼆)实现哲学家就餐问题1. 算法思想规定奇数号哲学家先拿他左边的筷⼦,然后再去拿右边的筷⼦,⽽偶数号哲学家则相反。
按此规定,将是1、2号哲学家竞争1号筷⼦;3、4号哲学家竞争3号筷⼦。
即五位哲学家都⽣竞争奇数号筷⼦,获得后,再去竞争偶数号筷⼦,最后总会有⼀位哲学家能获得两只筷⼦⽽进餐。
2. 流程图3. 程序代码(重要代码请注释)#include#include#include#include#include#define NOC 5 //number of chopstic#define NOP 5 //number of philosopher sem_t chopstic[NOC]; //semaphoreint flag[5]; //philosopher's statusvoid *eat(int i){int position;int temp = 0;int j = (i+1)%NOC;position = i%2;while(1){if(position == 0){ //odd take left first sem_wait(&chopstic[i]);sem_wait(&chopstic[j]);printf("philosopher%d get %d\n", i, j);flag[i] = 1; //philosopher is eatingprintf("waitting:"); //print others' statuswhile(temp < 5){if(!flag[temp])printf("philosopher%d\t", temp);temp++;}temp = 0;printf("\n");printf("eating:");// print others' statuswhile(temp < 5){if(flag[temp])printf("philosopher%d\t", temp);temp++;}printf("\n\n");temp = 0;//printf("\nphilosopher%d is eating\n\n", i); sleep(2);flag[i] = 0;printf("philosopher%d put %d\n", i, i); sem_post(&chopstic[i]); printf("philosopher%d put %d\n", i, j); sem_post(&chopstic[j]); }else{ //even take right firstsem_wait(&chopstic[j]);printf("philosopher%d get %d\n", i, j);sem_wait(&chopstic[i]);printf("philosopher%d get %d\n", i, i);flag[i] = 1;printf("waitting:");while(temp < 5){if(!flag[temp])printf("philosopher%d\t", temp);}temp = 0;printf("\n");printf("eating:");while(temp < 5){if(flag[temp])printf("philosopher%d\t", temp);temp++;printf("\n\n");temp = 0;//printf("\nphilosopher%d is eating\n\n", i);sleep(2);flag[i] = 0;printf("philosopher%d put %d\n", i, j);sem_post(&chopstic[j]);printf("philosopher%d put %d\n", i, i);sem_post(&chopstic[i]);}}}int main(void){int i = 0;int error;pthread_t philosopher[NOP];//init semwhile(i < 5){flag[i] = 0;sem_init(&chopstic[i], 0, 1);i++;}i = 0;//create threadwhile(i < 5){error = pthread_create(&philosopher[i], NULL, (void *)eat, (void *)i);printf("error:create thread failed!!\n");exit(0);}i++;}//destroy threadwhile(i < 5){pthread_join(philosopher[i], NULL);i++;}i = 0;//destroy semwhile(i < 5){sem_destroy(&chopstic[i]);i++;}return 0;}七、实验及结果分析:运⾏结果:udbwxfso@ubuntu:~/Desktop/sy2$ gcc gphilosopher.c -pthread udbwxfso@ubuntu:~/Desktop/sy2$ ./a.out philosopher4 get 4philosopher4 get 0waitting:philosopher0 philosopher1 philosopher2 philosopher3 eating:philosopher4philosopher2 get 2philosopher2 get 3waitting:philosopher0 philosopher1 philosopher3eating:philosopher2 philosopher4philosopher4 put 4philosopher4 put 0philosopher4 get 4philosopher4 get 0waitting:philosopher0 philosopher1 philosopher3eating:philosopher2 philosopher4philosopher2 put 3philosopher2 get 2philosopher2 get 3waitting:philosopher0 philosopher1 philosopher3eating:philosopher2 philosopher4philosopher4 put 4philosopher3 get 4philosopher4 put 0philosopher0 get 0philosopher0 get 1waitting:philosopher1 philosopher3 philosopher4eating:philosopher0 philosopher2philosopher2 put 2philosopher2 put 3philosopher2 get 2philosopher2 get 3waitting:philosopher1 philosopher3 philosopher4eating:philosopher0 philosopher2philosopher2 put 2philosopher2 put 3philosopher2 get 2philosopher2 get 3waitting:philosopher1 philosopher3 philosopher4eating:philosopher0 philosopher2经分析可知,放在桌⼦上的筷⼦是临界资源,在⼀段时间内只允许⼀位哲学家使⽤,为了实现对筷⼦的互斥使⽤,⽤⼀个信号量表⽰⼀只筷⼦,由这五个信号量组成信号量数组。
利⽤记录型信号量解决不会出现死锁的哲学家就餐问题试利⽤记录性信号量写出⼀个不会出现死锁的哲学家进餐问题的算法规定在拿到左侧的筷⼦后,先检查右⾯的筷⼦是否可⽤。
如果不可⽤,则先放下左侧筷⼦,等⼀段时间再重复整个过程。
分析:当出现以下情形,在某⼀个瞬间,所有的哲学家都同时启动这个算法,拿起左侧的筷⼦,⽽看到右侧筷⼦不可⽤,⼜都放下左侧筷⼦,等⼀会⼉,⼜同时拿起左侧筷⼦……如此这样永远重复下去。
对于这种情况,所有的程序都在运⾏,但却⽆法取得进展,即出现饥饿,所有的哲学家都吃不上饭。
(2) 描述⼀种没有⼈饿死(永远拿不到筷⼦)算法。
考虑了四种实现的⽅式(A、B、C、D):A.原理:⾄多只允许四个哲学家同时进餐,以保证⾄少有⼀个哲学家能够进餐,最终总会释放出他所使⽤过的两⽀筷⼦,从⽽可使更多的哲学家进餐。
以下将room 作为信号量,只允许4 个哲学家同时进⼊餐厅就餐,这样就能保证⾄少有⼀个哲学家可以就餐,⽽申请进⼊餐厅的哲学家进⼊room 的等待队列,根据FIFO 的原则,总会进⼊到餐厅就餐,因此不会出现饿死和死锁的现象。
伪码:semaphore chopstick[5]={1,1,1,1,1};semaphore room=4;void philosopher(int i){while(true){think();wait(room); //请求进⼊房间进餐wait(chopstick[i]); //请求左⼿边的筷⼦wait(chopstick[(i+1)%5]); //请求右⼿边的筷⼦eat();signal(chopstick[(i+1)%5]); //释放右⼿边的筷⼦signal(chopstick[i]); //释放左⼿边的筷⼦signal(room); //退出房间释放信号量room}}B.原理:仅当哲学家的左右两⽀筷⼦都可⽤时,才允许他拿起筷⼦进餐。
⽅法1:利⽤AND 型信号量机制实现:根据课程讲述,在⼀个原语中,将⼀段代码同时需要的多个临界资源,要么全部分配给它,要么⼀个都不分配,因此不会出现死锁的情形。
计算机操作系统典型例题解析之三【例1】分配到必要的资源并获得处理机时的进程状态是(B)。
A、就绪状态B、执行状态C、阻塞状态D、新状态分析:进程有三种基本状态:就绪状态、执行状态和阻塞状态。
当进程已分配到除CPU以外的所有必要的资源后,只要能再获得处理机便可立即执行,这时的状态称为就绪状态;处于就绪状态的进程如果获得了处理机,其状态转换为执行状态;进程因发生某种事件(如I/O请求、申请缓冲空间等)而暂停执行时的状态,亦即进程的执行受到阻塞,故称这种状态为阻塞状态;而新状态是指创建了进程但尚未把它插入到就绪队列前的状态。
所以本题的答案是B。
【例2】挂起的进程被激活,应该使用(C)原语。
A、CreateB、SuspendC、ActiveD、Wakeup分析:在不少系统中,进程除了三种基本状态外,又增加了一些新的状态,其中最重要的是挂起状态。
“挂起”的实质是使进程不能继续执行,即使挂起后的进程处于就绪状态,它也不能参加对CPU的竞争,进程的挂起调用Suspend()原语。
因此,被挂起的进程处于静止状态,相反,没有挂起的进程则处于活动状态。
而且,处于静止状态的进程,只有通过“激活”动作,调用Active()原语,才能转换成活动状态,调入内存。
所以本题的答案是C。
【例3】任何时刻总是让具有最高优先数的进程占用处理器,此时采用的进程调度算法是(D)。
A非抢占式的优先数调度算法B、时间片轮转调度算法C、先来先服务调度算法D、抢占式的优先数调度算法分析:“让具有最高优先数的进程占用处理器”,我们可以知道,采用的进程调度算法是优先数调度算法,但是我们还要进一步分析是抢占式的还是非抢占式的。
“任何时刻总让”,通过这句话我们知道采用的是抢占式的,所以本题的答案是D。
【例4】若P、V操作的信号量S初值为2,当前值为-1,则表示有(B)等待进程。
A、0个B、1个C、2个D、3个分析:信号量的初始值表示系统中资源的数目,每次的Wait操作意味着进程请求一个单位的资源,信号量进行减1的操作,当信号量小于0时,表示资源已分配完毕,进程自我阻塞。
进程管理一、设计目的、功能与要求设计目的:掌握进程管理的相关内容,对进程的同步和互斥,及信号量机制有深入的理解实验内容:模拟实现用信号量机制解决哲学家就餐问题具体要求:任选一种计算机高级语言编程实现实现5个哲学家(5只筷子)的顺利就餐需避免出现死锁使用信号量、及信号量的等待队列使用P操作、V操作能够显示每个哲学家、及每只筷子的状态二、问题的详细描述、需求分析该实验要求模拟实现用信号量机制解决哲学家就餐问题。
哲学家就餐问题详细描述如下:有五个哲学家围坐在一圆桌旁,桌中央有一盘通心粉,每人面前有一只空盘子,有五个哲学家围坐在一圆桌旁,桌中央有一盘通心粉,每人面前有一只空盘子,每两人之间放一只筷子,即共5只筷子。
每个哲学家的行为是思考和进餐。
每个哲学家的行为是思考和进餐。
为了进餐,每个哲学家必须拿到两只筷子,并且每个人只能直接从自己的左边或右边去取筷子。
思考时则同时将两支筷子放回原处时则同时将两支筷子放回原处。
规则:只有拿到两只筷子时,哲学家才能吃饭;只有拿到两只筷子时,哲学家才能吃饭;如果筷子已经在他人手上,则该哲学家必须等到他人吃完之后才能拿到筷子;如果筷子已经在他人手上,则该哲学家必须等到他人吃完之后才能拿到筷子;任何一个哲学家在自己没有拿到两只筷子吃饭之前,决不放下自己手中的筷子。
为了解决哲学家就餐问题同时解决死锁现象,需要设计一定的解决策略。
有这样几种解决办法:1.至多只允许四个哲学家同时进餐,以保证至少有一个哲学家可以进餐,最终总会释放出他所用过的两只筷子,从而可使更多的哲学家进餐;2.仅当左右两只筷子均可用时,才允许哲学家拿起筷子就餐;3.规定奇数号哲学家先拿起右边筷子,然后再去拿左边筷子,而偶数号哲学家则相反。
该实验我采取了第2种解决方案,每个哲学家在确定自己左右两边的筷子都能用时,才能同时拿起两只筷子进餐,进餐完成后同时放下两只筷子。
三、数据结构、功能设计(功能与框图、功能模块说明)该实验使用java的多线程,同时利用到关键字synchronized实现了共享数据的互斥锁定,以保证每只筷子的状态的互斥的。
操作系统实验报告实验名称:哲学家就餐院系:电子与信息工程系班级:电信学号:姓名:一.实验目的1.理解信号与PV操作,并学会掌握解决哲学家就餐问题2.理解资源分配,和防止死锁的方法策略。
3.熟练使用 VC++6.0 或者codeblock编译环境,调试并正确运行程序。
二、实验原理1、问题描述:有五个哲学家围坐在一圆桌旁,桌中央有一盘通心粉,每人面前有一只空盘子,每两人之间放一只筷子每个哲学家的行为是思考,感到饥饿,然后吃通心粉.为了吃通心粉,每个哲学家必须拿到两只筷子,并且每个人只能直接从自己的左边或右边去取筷子。
当筷子(资源)可用时,先分配左边的筷子,等待一会后再分配右边的筷子,由于这个过程中,左边的筷子一直没有释放,就有可能产生死锁了,即每个哲学家同时拿起左边的筷子,但是他们都无法获得右手边的筷子,于是释放左手边的筷子;等待后重复同时拿起左手边筷子,无法获得右手边的筷子……导致所有人饥饿。
从而产生死锁2、程序运行说明有5位哲学家,共享一张放有5把椅子的桌子,每人分得一把椅子。
但是,桌上总共有5支筷子,在每人两边分开各放一支。
哲学家只有在饥饿时方可分两次从两边抢占筷子就餐。
就餐的条件如下:①哲学家想吃饭时,先提出吃饭请求。
②提出吃饭请求时,并拿到两支筷子后,方可吃饭。
③如筷子已被他人获得,则必须等待此人吃饭后才能获取筷子。
④任一哲学家在自己未拿到两支筷子吃饭之前,决不放下手中的筷子。
⑤刚开始就餐时,只允许两个哲学家吃饭。
三.实验内容及分析本实验主要需要考虑的是筷子的分配,防止死锁以及解饿现象。
分别模拟死锁和防死锁模式。
五个人五支筷子即最多真有两个人同时就餐。
若出现哲学家同时饥饿的现象,这时他们同时拿起自己左边的筷子,当他们去拿右边的筷子时发现右边的筷子已经被自己旁边的人拿走,这时便产生了死锁,每个哲学家都无法获得一双筷子。
为了保证不会出现相邻的两个哲学家同时提出吃饭的请求,本实验特设置信号量S[0]~S[4]来互斥两两相邻的哲学家吃饭请求,其初值均为1。
哲学家就餐问题的算法实现在哲学家就餐问题中,假定有五位哲学家围坐在一张圆桌旁,每位哲学家面前都放着一碗意面和一把叉子。
这五位哲学家中的每位都有两种活动,一种是思考,一种是就餐。
但是,这五位哲学家只有五支叉子共用,也就是说,每个哲学家只能在自己左右两边分别拿起一把叉子。
若这五位哲学家中有若干人同时拿着左手边的叉子,或者同时拿着右手边的叉子,那么他们都无法就餐。
这时,就需要找到一种算法来解决这个问题。
解法一:Chandy/Misra解法Chandy/Misra解法是一种分布式算法,它的思路是使得每一位哲学家都向右边的人要叉子。
一旦它们都拿到了右边的叉子,就会开始就餐,然后把左右两个叉子都放回去。
这种算法的优点是它具有分布式系统的特点,不会造成资源竞争,而且在算法处理结束后,所有人都不会处于饥饿状态。
但是,这种算法实现的难度比较大,因为需要每个哲学家都向右边的人要叉子,而且需要考虑异常情况。
解法二:Dijkstra解法Dijkstra解法也被称为银行家算法,它的思路是让每一位哲学家先拿一支叉子,只有拿到两支叉子后,才可以开始就餐。
一旦就餐结束,哲学家就将叉子放回去。
这种算法的优点是它比较简单,易于实现。
但是,如果有一位哲学家先拿了一支叉子并且不再释放,那么其他哲学家就会陷入死锁状态。
解法三:Semaphore解法Semaphore解法是以信号量为基础的算法,它的思路是对每支叉子定义一个信号量,并使用信号量来控制哲学家与叉子之间的关系。
一旦一个哲学家想要就餐,就需要请求两个叉子的信号量,如果请求不到,则会被阻塞。
这种算法的优点是它具有可扩展性,而且可以支持多个进程同时使用资源。
但是,它比较复杂,可能会导致死锁。
结论在实际开发中,我们可以根据自己的需求选择合适的算法来解决哲学家就餐问题。
如果要求系统具有分布式系统的特点,可以使用Chandy/Misra解法;如果要求简单易行和易于实现,可以使用Dijkstra解法;如果要求可扩展性和支持多进程,可以选择Semaphore解法。
《操作系统》实验报告实验题目:哲学家进餐问题一、实验目的:实现linux下哲学家进餐问题。
体会如何控制进程同步与互斥的信号量方法。
二、实验内容:运用linux下fork,IPC信号量机制中的相关系统调用,IPC共享存储器机制,实现哲学家进餐问题。
三、编程环境及工具Linux下GCC四、具体设计要求及有关说明Fork() 在子进程刚被创建时,它和父进程具有相同的共享正文段,而其他进程信息则完全拷贝父进程得到。
信号量调用的示例程序:#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>#include <stdio.h>union semun {int val;struct semid_ds *buf;ushort * array;}argument;int main(){int id; /* Number by which the semaphore is known within a program */argument.val = 0;//新建一个信号量集合,包含一个信号量id = semget(IPC_PRIV ATE, 1, 0666 | IPC_CREAT);if(id < 0){printf("Unable to obtain semaphore.\n ");exit(-1);}//将信号量的值置为0if( semctl(id, 0, SETV AL, argument) < 0){printf("Cannot set semaphore value.\n");}else{printf("Semaphore initialized.\n");}}int lock_sem(int semid, int member){/*初始化sembuf结构,将指定信号量值减1。
哲学家就餐问题——and型信号量机制仅当哲学家的左、右两只筷⼦均可⽤时,才允许他拿起筷⼦进餐以哲学家进餐模型为依据,在Linux控制台环境下创建5个进程,⽤semget函数创建⼀个信号量集(5个信号量,初值为1),模拟哲学家的思考和进餐⾏为:每⼀位哲学家饥饿时,先拿起左⼿筷⼦,再拿起右⼿筷⼦;筷⼦是临界资源,为每⼀⽀筷⼦定义1个互斥信号量;想拿到筷⼦需要先对信号量做P操作,使⽤完释放筷⼦对信号量做V操作。
实验平台:操作系统:CentOS 7编辑器:Gedit | Vim编译器:Gcc#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdint.h>#include <stdbool.h>#include <errno.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/ipc.h>#include <sys/sem.h>#include <sys/wait.h>#ifdef _SEM_SEMUN_UNDEFINEDunion semun{int val;struct semid_ds *buf;unsigned short *array;struct seminfo *__buf;};#endif#define ERR_EXIT(m) \do { \perror(m); \exit(EXIT_FAILURE); \} while(0)//相当于P操作int wait_1chopstick(int no,int semid){struct sembuf sb = {no,-1,0};int ret;ret = semop(semid,&sb,1);//semop()系统调⽤在semid标识的信号量集中的信号量上执⾏⼀个或多个up或down操作,可⽤于进程间的同步和互斥。
操作系统哲学家就餐问题实验报告实验目的:通过实验探究操作系统中的哲学家就餐问题,了解并掌握解决该问题的不同算法。
实验原理:哲学家就餐问题是一个典型的并发控制问题,该问题描述了一群哲学家围坐在圆桌上,每个哲学家左右两边各有一只筷子,哲学家只有同时拿到左右两只筷子时才能进餐,进餐完毕后将筷子放回桌面。
该问题的解决涉及到互斥、死锁、饥饿等并发问题。
实验步骤:1. 实现基于信号量的解法:- 为每个哲学家和筷子创建一个信号量;- 哲学家进入就餐前先检查左右两只筷子是否可用;- 若可用,则将左右两只筷子设置为不可用,并进餐;- 进餐完毕后将左右两只筷子设置为可用。
2. 实现基于管程的解法:- 哲学家类中定义进餐和放下筷子的方法;- 使用一个互斥锁来确保每次只有一个哲学家能进入管程;- 当某个哲学家想要进餐时,先检查是否有足够的筷子可用;- 若可用,则进入进餐方法,将筷子设置为不可用,并开始进餐; - 进餐完毕后,释放筷子并通知其他哲学家。
3. 运行实验程序,观察哲学家的就餐过程。
4. 分析实验结果,比较两种算法的优缺点。
实验结果与分析:通过观察实验程序运行的结果,可以发现在基于信号量的解法中,哲学家的就餐过程是以并发的方式进行的,每个哲学家在不产生死锁的前提下获取到筷子并进餐。
但是,由于每个哲学家只能同时拿到一只筷子,有可能会出现饥饿的情况,即某个哲学家一直无法获取到筷子进餐。
而基于管程的解法则能够避免饥饿的问题,每个哲学家在进餐前都会检查是否有足够的筷子可用,若不可用则等待。
通过互斥锁的使用,确保每次只有一个哲学家能够进入管程进行操作,避免了并发产生的问题。
综上所述,基于管程的解法相对于基于信号量的解法更加可靠,能够避免饥饿问题的出现。
但是在实际应用中,两种解法的选择还需要根据具体情况进行权衡和取舍。
有效的解决哲学家就餐问题的方法有效的解决哲学家就餐问题的方法引言哲学家就餐问题是一个经典的并发算法问题,描述了五个哲学家围坐在圆桌旁,需要通过共享的筷子来进行进餐的场景。
在这个问题中,每个哲学家需要交替地进行思考和进餐,而他们的进餐需要两根筷子。
解决方法为了有效地解决哲学家就餐问题,我们可以采用以下几种方法:1.资源分级分配:引入一个资源分级机制,确保每个哲学家在进餐时仅能获得特定的筷子,而非两根。
这样一来,将不会出现所有哲学家同时拿起自己左边的筷子的情况,从而避免死锁。
2.奇偶策略:将哲学家分为奇数和偶数两组,奇数组先尝试获取左边的筷子,偶数组先尝试获取右边的筷子。
通过交替获取筷子的方式,可以避免所有哲学家同时获取同一边筷子的情况,降低死锁可能性。
3.资源分配顺序:当一个哲学家准备就餐时,他需要按照特定的顺序获取筷子,而不是随机选择。
通过确定筷子获取的顺序,可以规避潜在的死锁情况。
4.资源抢占:引入资源抢占的机制,当一个哲学家尝试获取筷子时,如果发现对应的筷子已被其他哲学家占用,他可以选择尝试获取其他筷子,或者等待一段时间后再次尝试。
这样可以有效避免死锁,并提高吞吐量。
5.动态调整资源:根据当前进餐的哲学家数量,动态调整可用的筷子资源。
当哲学家数量较少时,可以减少筷子的个数,避免资源浪费;当数量较多时,则需要增加筷子的个数,保证所有哲学家都能同时用餐。
结论哲学家就餐问题是一个常见的并发问题,通过采用上述方法中的一种或多种,可以有效解决死锁和资源浪费的问题,提高整体的系统效率和可靠性。
在实际应用中,需要根据场景的具体需求和系统特点选择合适的方法,并进行适当优化。
6.基于信号量的解决方案:使用信号量作为同步工具,每个哲学家尝试获取筷子时需要先获取两个信号量,表示左右两只筷子的可用性。
当某个哲学家无法获取到两个信号量时,则放弃当前的操作,等待一段时间后再次尝试。
这种方法可以有效防止死锁的发生。
7.餐位限制:通过限制同时进餐的哲学家人数来解决就餐问题。
利用信号量机制解决哲学家进餐问题————————————————————————————————作者:————————————————————————————————日期:成绩:课程设计报告课程名称:课程设计(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 函数动作 TKFKPut_forks 函数动作 PTFKTest 函数动作 TESTTHINKING 思考状态 THINKEATING 进餐状态 EATNYNYY N五、 原程序清单#include<stdio.h>#include<unistd.h>#include<assert.h>#include<sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>#include<assert.h>#include<sys/shm.h>#include<signal.h>#include<stdlib.h>#define N 5#define LEFT(i) (i+N-1)%N#define RIGHT(i) (i+1)%NTKFK TEST PTFK#define THINKING 0#define EATING 1//#include"behavior_philosophy.h"void philosopher(int , char*, int , int , void (*)(int, int), void (*)(int, int)); void take_forks(int , char* , int , int ,void (*)(int, int), void (*)(int, int)); void put_forks(int , char* , int , int ,void (*)(int, int), void (*)(int, int)); void test(int , char* , int ,void (*)(int, int));void think(int );void eat(int );void P(int , int );void V(int , int );/*------------------------哲学家动作-----------------*/void philosopher(int i, char* state, int sem_phiid, int sem_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);}}void take_forks(int i, char* state, int sem_phiid, int sem_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);}void put_forks(int i, char* state, int sem_phiid, int sem_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);}void test(int i, char* state, int sem_phiid,void (*V)(int, int)){if(state[i] == THINKING && state[LEFT(i)] != EATING && state[RIGHT(i)]!=EATING){state[i] = EATING;(*V)(sem_phiid, i);}}void think(int i){printf("philosopher:%d>>>>>is THINKING.\n", i);}void eat(int i){printf("I'm philosopher:%d>>>>>is EATING.and will eating %d seconds!\n", i, sleep(2));}/*---------------------P,V原子操作------------*/void P(int semid, int index){struct sembuf sema_buffer;sema_buffer.sem_num = index;sema_buffer.sem_op = -1;sema_buffer.sem_flg = SEM_UNDO;semop(semid, &sema_buffer, 1);}void V(int semid, int index){struct sembuf sema_buf;sema_buf.sem_num = index;sema_buf.sem_op = 1;sema_buf.sem_flg = SEM_UNDO;semop(semid, &sema_buf, 1);}/*-----------------------------------------------------------*/int main( ){/*-------------------------创建信号量操作----------------*/int sem_phiid;sem_phiid = semget(1008, 5, 0666|IPC_CREAT);assert(sem_phiid>=0);unsigned short array[5] = {0, 0, 0, 0, 0};union semun{int val;unsigned short *array;}semopts;semopts.array=array;int ret1 = semctl(sem_phiid, 0, SETALL, semopts);assert(ret1 == 0);int sem_mutexid;sem_mutexid = semget(0x225, 1, 0666|IPC_CREAT);assert(sem_mutexid >= 0);semopts.val = 1;int ret2 = semctl(sem_mutexid, 0, SETVAL, semopts); assert(ret2 == 0);/*------------------初始化共享内存-----------------------*/ int shmid;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("shmget faild!");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("shmat faild!");exit(1);}/*-----------------------------------------------*/int i, phinum;pid_t pid;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, GAME OVER!");}elsephilosopher(phinum, state, sem_phiid, sem_mutexid, P, V); }六、程序运行结果截图一截图二截图三七、设计总结这次课程设计通过利用UNIX系统的信号量机制和共享内存机制,编写一个解决哲学家进餐问题的程序,感觉有很多收获,主要体现在以下几点:(1)很好的复习了UNIX系统编程课上所学的知识,尤其是对unix进程通信机制中信号量机制和共享内存机制有了实践和更深的认识。