操作系统第6次实验报告:使用信号量解决进程互斥访问
- 姓名:林永鑫
- 学号:201821121040
- 班级:计算1812
1. 选择哪一个问题
选题哪个问题?
- 生产者-消费者问题
- 读者-写者问题
- 哲学家进餐问题
选择哲学家进餐问题
关于此问题:
有五个哲学家,他们的生活方式是交替地进行思考和进餐,哲学家们共用一张圆桌,分别坐在周围的五张椅子上,
在圆桌上有五个碗和五支筷子,平时哲学家进行思考,饥饿时便试图取其左、右最靠近他的筷子,只有在他拿到
两支筷子时才能进餐,该哲学家进餐完毕后,放下左右两只筷子又继续思考。
约束条件
(1)只有拿到两只筷子时,哲学家才能吃饭。
(2)如果筷子已被别人拿走,则必须等别人吃完之后才能拿到筷子。
(3)任一哲学家在自己未拿到两只筷子吃完饭前,不会放下手中已经拿到的筷子。
2. 给出伪代码
程序中,哲学家编号为0到4,筷子编号也为0到4且与哲学家编号相同的筷子位于该哲学家左侧。
为了解决死锁的问题:仅当哲学家左右两侧的筷子都可用时,才允许他一次性同时拿起两只筷子。
semaphore chopstick chopstick[5] = {1,1,1,1,1}; do { //think Sswait(chopstick[i],chopstick[(i+1)%5]); //eat Ssignal(chopstick[i],chopstick[(i+1)%5]); }while(true)
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. 运行结果并解释
运行结果:代码一直运行,不停止。说明没有出现死锁
第一行到第五行:5位哲学家都在思考中
第六行到第七行:编号为3的哲学家饿了,此时该哲学家左右两侧的筷子都可用,他一次性同时拿起两只筷子,开始进食
第八行:编号为2的哲学家饿了,此时该哲学家右侧的筷子被编号为3的哲学家使用中,所以该哲学家保持饥饿
第九行:编号为0的哲学家饿了,此时该哲学家左右两侧的筷子都可用,他一次性同时拿起两只筷子,开始进食
第十行:编号为4的哲学家饿了,此时该哲学家左侧的筷子被编号为3的哲学家使用中,所以该哲学家保持饥饿
第十一行:编号为0的哲学家进食结束,恢复思考状态
第十二行到第十三行:编号为1的哲学家饿了,此时该哲学家左右两侧的筷子都可用,他一次性同时拿起两只筷子,开始进食
第十四行到第十五行:编号为3的哲学家进食结束,恢复思考状态。此时编号为4的哲学家左右两侧的筷子都可用,他一次性同时拿起两只筷子,开始进食
第十六行:编号为0的哲学家饿了,此时该哲学家右侧的筷子被编号为1的哲学家使用中,所以该哲学家保持饥饿
第十七行:编号为4的哲学家进食结束,恢复思考状态
第十八行到第十九行:编号为3的哲学家饿了,此时该哲学家左右两侧的筷子都可用,他一次性同时拿起两只筷子,开始进食
第二十行:编号为0的哲学家进食结束,恢复思考状态