- 姓名 胡家揆
- 学号 201821121023
- 班级 计算1811
1. 选择哪一个问题
- 哲学家进餐问题
2. 给出伪代码
原理:至多只允许四个哲学家同时进餐,以保证至少有一个哲学家能够进餐,最终总会释
放出他所使用过的两支筷子,从而可使更多的哲学家进餐。以下将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 } }
3. 给出完整代码
#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> union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *__buf; }; #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) //申请一个资源 int wait_1fork(int no,int semid) { //int left = no; //int right = (no + 1) % 5; struct sembuf sb = {no,-1,0}; int ret; ret = semop(semid,&sb,1); if(ret < 0) { ERR_EXIT("semop"); } return ret; } // 释放一个资源 int free_1fork(int no,int semid) { struct sembuf sb = {no,1,0}; int ret; ret = semop(semid,&sb,1); if(ret < 0) { ERR_EXIT("semop"); } return ret; } //这里表明叉子是一个临界资源 #define DELAY (rand() % 5 + 1) //相当于P操作 void wait_for_2fork(int no,int semid) { //哲学家左边的刀叉编号和哲学家是一样的 int left = no; //右边的刀叉 int right = (no + 1) % 5; //刀叉值是两个 //注意第一个参数是编号 //操作的是两个信号量,即两种资源都满足,才进行操作 struct sembuf buf[2] = { {left,-1,0}, {right,-1,0} }; //信号集中有5个信号量,只是对其中的资源sembuf进行操作 semop(semid,buf,2); } //相当于V操作 ,释放刀叉 void free_2fork(int no,int semid) { int left = no; int right = (no + 1) % 5; struct sembuf buf[2] = { {left,1,0}, {right,1,0} }; semop(semid,buf,2); } //哲学家要做的事 void philosophere(int no,int semid) { srand(getpid()); //srand(time(NULL)); for(;;) { #if 1 //这里采取的措施是当两把刀叉都可用的时候(即两种资源都满足的时候) //哲学家才能吃饭,这样不相邻的哲学家就可吃上饭 printf("%d is thinking\n",no); // 思考中 sleep(DELAY); printf("%d is hungry\n",no); // 感觉到饥饿 wait_for_2fork(no,semid);//拿到两把叉子才能吃饭 printf("%d is eating\n",no); // 吃饭 sleep(DELAY); free_2fork(no,semid);//释放两把叉子 #else //这段代码可能会造成死锁 int left = no; int right = (no + 1) % 5; printf("%d is thinking\n",no); // 思考中 sleep(DELAY); printf("%d is hungry\n",no); // 感觉到饥饿 wait_1fork(left,semid); // 拿起左叉子,现在是只要有一个资源,就申请 sleep(DELAY); wait_1fork(right,semid); // 拿到右叉子 printf("%d is eating\n",no); // 吃饭 sleep(DELAY); free_1fork(left,semid); // 释放左叉子 free_1fork(right,semid); // 释放右叉子 #endif } } int main(int argc,char *argv[]) { int semid; //创建信号量 //信号量集中5个信号量 semid = semget(IPC_PRIVATE,5,IPC_CREAT | 0666); if(semid < 0) { ERR_EXIT("semid"); } union semun su; su.val = 1; int i; for(i = 0;i < 5;++i) { //注意第二个参数也是索引 semctl(semid,i,SETVAL,su); } //创建4个子进程 int num = 0; pid_t pid; for(i = 1;i < 5;++i) { pid = fork(); if(pid < 0) { ERR_EXIT("fork"); } if(0 == pid) // 子进程 { num = i; break; } } //这里就是哲学家要做的事情 philosophere(num,semid); return 0; }
4. 运行结果并解释
哲学家:0 1 2 3 4 5 五个人
5个人一开始都进入思考状态
1开始吃、4也开始吃此时两个人在吃且不相邻,其他三位处于饥饿的等待状态w
然后4开始思考(释放进程),3开始吃(使用资源),1吃完了0开始吃……
……
……
始终满足哲学家进餐问题的条件:筷子够用
在拿起筷子前先判断左右两个筷子是否可用,可用才能拿,而且是同时拿,这样不相邻的哲学家就可以吃上饭,不会造成死锁。