操作系统第6次实验报告:使用信号量解决进程互斥访问
- 姓名:林顺达
- 学号:201821121022
- 班级:计算1811
1. 哲学家进餐问题
问题描述:有五个哲学家,他们的生活方式是交替地进行思考和进餐,哲学家们共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五支筷子,平时哲学家进行思考,饥饿时便试图取其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐,该哲学家进餐完毕后,放下左右两只筷子又继续思考
2. 给出伪代码
伪代码:
void philosopher(哲学家编号,从0到4) { while(真) { 哲学家思考; 饿了,拿起左叉子,更改左边信号量; 拿起右叉子,更改右边信号量; 进食; 放下左叉子; 放下右叉子; } }
这样可以保证不会有相邻的哲学家同时进食,但当五位哲学家都拿起左叉子时会引起死锁,由于五个叉子的信号量为0,但同时又等待右叉子,而又无空余的叉子,此时就进入了无限等待状态
对于上述死锁解决方法:同时拿起左右叉子,并且同时放下,伪代码:
void philosopher(哲学家编号,从0到4) { while(真) { 哲学家思考; 饿了,同时拿起左右叉子,同时更改左右信号量 进食; 同时放下左右叉子,同时更改左右信号量 } }
3. 给出完整代码
#include<stdio.h> #include<stdlib.h> #include<sys/ipc.h> #include<sys/msg.h> #include<sys/types.h> #include<unistd.h> #include<errno.h> #include<sys/ipc.h> #include<sys/sem.h> #include<sys/wait.h> #define ERR_EXIT(m) \ do { \ perror(m); \ exit(EXIT_FAILURE); \ } while(0) #define SEMS_NUM 5 //信号量数目 #define THINKER_NUM 5 //哲学家人数 #define FORK_NUM 5 //叉子数 #define TIME_WAIT (rand() % 5 + 1) //等待时间 union semun { int val; //SETVAL用的值 struct semid_ds *buf; //IPC_STAT、IPC_SET用的semid_ds结构 unsigned short *array; //SETALL、GETALL用的数组值 struct seminfo *__buf; //为控制IPC_INFO提供的缓存 }; /*对信号量数组编号为no的信号量做P操作 哲学家拿取两把叉子,如果未拿到则进餐受阻 */ void P(int sem_id,int left_fno,int right_fno) { /* sbuf.sem_num:序号 sbuf.sem_op:操作,-1表示P操作 sem_flg:设置信号量的操作 */ struct sembuf sbuf[2] = { {left_fno, -1, SEM_UNDO}, {right_fno, -1, SEM_UNDO} }; if(semop(sem_id, sbuf, 2) == -1) { ERR_EXIT("P"); } } /*
对信号量数组编号为no的信号量做V操作 哲学家放回叉子 */ void V(int sem_id,int left_fno,int right_fno){ /* sbuf.sem_num:序号 sbuf.sem_op:操作,1表示V操作 sem_flg:设置信号量的操作 */ struct sembuf sbuf[2] = { {left_fno, 1, SEM_UNDO}, {right_fno, 1, SEM_UNDO} }; if(semop(sem_id, sbuf, 2) == -1) { ERR_EXIT("V"); } } void think(int tk_no){ printf("philosopere%d is thinking\n", tk_no); sleep(TIME_WAIT); } void eat(int tk_no){ printf("philosopere%d is eating\n", tk_no); sleep(TIME_WAIT); } void hungry(int tk_no){ printf("philosopere%d is hungry,need eat\n", tk_no); } void philosopere(int sem_id,int tk_no) { srand(getpid()); int left_fno=tk_no; //left_fno左边叉子编号 int right_fno=(left_fno+1)%FORK_NUM; while(1) { think(tk_no);//思考 hungry(tk_no);//饥饿 P(sem_id,left_fno,right_fno);//拿取一双叉子 eat(tk_no);//就餐 V(sem_id,left_fno,right_fno);//放下叉子 } } int main() { /*
1.创建 Semaphore(计数信号量) 创建信号集,其中有SEMS_NUM个信号量
*/ int sem_id = semget(IPC_PRIVATE, SEMS_NUM, IPC_CREAT | 0666); if (sem_id == -1){ ERR_EXIT("semget"); } //2.初始化 Semaphore union semun sem; sem.val = 1; int i; for (i = 0; i < SEMS_NUM; i++){ if(semctl(sem_id, i, SETVAL, sem)==-1){ ERR_EXIT("semctl"); } } int tk_no = 0;//哲学家编号 pid_t pid; //创建子进程 for (i = 1; i < THINKER_NUM; i++) { pid = fork(); if (pid == -1){ ERR_EXIT("fork"); } if (pid == 0){ tk_no = i; break; } } philosopere(sem_id,tk_no);//处理就餐 return 0; }
4. 运行结果并解释
结果解释:
共有5个哲学家,对应着5个进程,其中针对上述死锁问题的改进,程序一开始为4、2(不相临的)拿起筷子并且进食,后来吃完后4进入思考状态,接着3、4处于饥饿状态需要进食,由于此时2还在eating,故3需要等待,而4继续进食,接下来的事件均以此类推