操作系统第6次实验报告:使用信号量解决进程互斥访问
- 姓名:倪晓东
- 学号:201821121020
- 班级:计算1811
1. 选择哪一个问题
- 哲学家进餐问题
2. 给出伪代码
问题描述:
有五个哲学家,他们的方式是交替地进行思考和进餐,哲学家们共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五支筷子,平时哲学家进行思考,饥饿时便试图取其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐,该哲学家进餐完毕后,放下左右两只筷子又继续思考。
约束条件:
(1)只有拿到两只筷子时,哲学家才能吃饭。
(2)如果筷子已被别人拿走,则必须等别人吃完之后才能拿到筷子。
(3)任一哲学家在自己未拿到两只筷子吃完饭前,不会放下手中已经拿到的筷子。
主要矛盾:
因为一共只有5只筷子,不可能让所有哲学家同时进餐,筷子相对于临界资源,如果有一位哲学家在进餐,那么他的旁边两位哲学家就不可能也在进餐,如果5位哲学家同时拿起自己左手或者右手的筷子,那么就会因为缺少一只筷子进入等待状态,而又没有哲学家在进餐,所以不会有其他哲学家放下自己的筷子,就陷入一种死锁状态。
解决方法:
我们对哲学家进行编号,规定进餐时奇数哲学家依次拿起自己左边的筷子,偶数哲学家依次拿起自己右边的筷子,如果没有则进入等待状态,最后会有一个哲学家可以得到两只筷子可以进餐,该哲学家进餐结束后,释放两只筷子,会有另一位哲学家可以得到两只筷子进餐,依此类推。
void philosopher(unsigned short int phi_no) { int left = phi_no; //记录左筷子编号 int right = (phi_no + 1) % 5; //记录右筷子编号 while(1) { printf("哲学家%d正在思考问题\n", phi_no); printf("哲学家%d饿了\n", phi_no); if(phi_no % 2 == 0) { //判断奇数偶数,偶数先右后左 P(right); //哲学家拿起右手筷子 P(left); //哲学家拿起左手筷子 printf("哲学家%d正在就餐\n", phi_no);//进餐结束后放下筷子 V(left); //哲学家放下左手筷子 V(right); // 哲学家放下右手筷子 } else { //奇数先左后右 P(left); //哲学家拿起左手筷子 P(right); //哲学家拿起右手筷子 printf("哲学家%d正在就餐\n", phi_no);//进餐结束后放下筷子 V(right); //哲学家放下右手筷子 V(left); //哲学家放下左手筷子 } } }
3. 给出完整代码
#include<stdio.h> #include<stdlib.h> #include<malloc.h> #include<time.h> #include<sys/ipc.h> #include<sys/msg.h> #include<sys/types.h> #include<unistd.h> #include<pthread.h> #include<semaphore.h> #include<errno.h> #include<sys/ipc.h> #include<sys/sem.h> #include<sys/wait.h> #ifndef _SEMUN_H #define _SEMUN_H union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* array for GETALL, SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; #endif int sem_id; //拿起筷子 void P(unsigned short int phi_no)//P操作,该筷子被人使用 { struct sembuf sbuf; sbuf.sem_num = phi_no; /*序号*/ sbuf.sem_op = -1; //对被使用的筷子进行标记 sbuf.sem_flg = SEM_UNDO; semop(sem_id, &sbuf, 1); } //放下筷子 void V(unsigned short int phi_no)//V操作,筷子被人放下,无人使用 { struct sembuf sbuf; sbuf.sem_num = phi_no; /*序号*/ sbuf.sem_op = 1; //对未被使用的筷子进行标记 sbuf.sem_flg = SEM_UNDO; semop(sem_id, &sbuf, 1); } void philosopher(unsigned short int phi_no) { int left = phi_no; //记录左筷子编号 int right = (phi_no + 1) % 5; //记录右筷子编号 while(1) { printf("哲学家%d正在思考问题\n", phi_no); printf("哲学家%d饿了\n", phi_no); if(phi_no % 2 == 0) { //判断奇数偶数,偶数先右后左 P(right); //哲学家拿起右手筷子 P(left); //哲学家拿起左手筷子 printf("哲学家%d正在就餐\n", phi_no);//进餐结束后放下筷子 V(left); //哲学家放下左手筷子 V(right); // 哲学家放下右手筷子 } else { //奇数先左后右 P(left); //哲学家拿起左手筷子 P(right); //哲学家拿起右手筷子 printf("哲学家%d正在就餐\n", phi_no);//进餐结束后放下筷子 V(right); //哲学家放下右手筷子 V(left); //哲学家放下左手筷子 } } } int main() { key_t key = ftok("/home", 1); if (key == -1){ printf("ftok error.\n"); exit(1); } // 创建信号量集,其中只有一个信号量 sem_id = semget(key, 1, IPC_CREAT|0666); if(sem_id == -1) { printf("semget error.\n"); exit(1); } union semun arg; arg.val = 1; if(semctl(sem_id, 0, SETVAL, arg) == -1) { printf("semctl error.\n"); exit(1); } unsigned short int phi_no; //number of philosopher pid_t pid; for (int i = 0; i < 5; i++){ pid = fork(); if (pid == -1) { printf("fork error.\n"); exit(1); } if (pid == 0) { phi_no = i; break; } } philosopher(phi_no); return 0; }
4. 运行结果并解释
对哲学家进行编号0,1,2,3,4。
当3,4号哲学家饿了,3号哲学家先拿起左手边筷子,3号哲学家左手边坐着4号哲学家,4号哲学家应该拿起自己右手边筷子,但是该筷子在3号哲学家手里,所以4号进入等待,3号可以继续拿起自己右手边筷子进餐。
4号哲学家进餐结束,放下筷子,3号饿了,可以拿起自己左手边筷子再拿右手边筷子,进行进餐,与1号哲学家没有资源冲突。
5. 参考资料
https://baike.sogou.com/v70137267.htm?fromTitle=%E5%93%B2%E5%AD%A6%E5%AE%B6%E8%BF%9B%E9%A4%90%E9%97%AE%E9%A2%98
https://www.cnblogs.com/biglucky/p/4633706.html