操作系统第6次实验报告:使用信号量解决进程互斥访问
- 姓名:白晓
- 学号:201821121035
- 班级:计算1812
1. 选择哪一个问题
- 哲学家进餐问题
五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在桌子上有五只碗和五只筷子,他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐毕,放下筷子继续思考。
2. 给出伪代码
给出伪代码,并解释算法思想。
semaphore fork[5]={1, 1,1,1,1}; semaphore room=4; void philosopher(int i) { while[(true) { think(); wait(room); /*请求进入房间进餐*/ wait(fork[i); /*请求左手边的筷子*/ wait(fork[(i+ 1)%5]);/*请求右手边的筷子*/ eat(); signal(fork[(i+ 1)%5]); //释放右手边的筷子 signal(fork[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) //相当于p操作 int wait_1chopstick(int no,int semid) { struct sembuf sb = {no,-1,0}; int ret= semop(semid,&sb,1); //semop()系统调用在semid标识的信号量集中的信号量上执行一个或者多个up/down操作,用于进程间的同步或互斥 if(ret < 0) { ERR_EXIT("semop"); } return ret; }//相当于v操作 int free_1chopstick(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_2chopstick(int no,int semid){ //哲学家左边的筷子号数 int left = no; //右边的筷子 int right = (no+1) % 5; //筷子值为2,第一个参数为编号 struct sembuf buf[2] = { {left,-1,0}, //左右两只筷子都能用时才进餐 {right,-1,0} };//信号集中有5个信号量,只是对其中的资源sembuf进行操作 semop(semid,buf,2); }//相当于V操作 void free_2chopstick(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()); for(;;){ #if 1 //两边的筷子都可以使用时,哲学家可以吃饭,不相邻的科学家就可以吃饭 printf("%d 思考中\n",no); sleep(DELAY); printf("%d 饿了\n",no); wait_for_2chopstick(no,semid);//拿到筷子才能吃饭 printf("%d 进餐中\n",no); sleep(DELAY); free_2chopstick(no,semid);//释放筷子 #else //可能会造成死锁 int right = (no + 1) % 5; printf("%d 思考中\n",no); sleep(DELAY); printf("%d 饿了\n",no); wait_1chopstick(left,semid); sleep(DELAY); wait_1chopstick(right,semid); printf("%d 进餐中\n",no); sleep(DELAY); free_1chopstick(left,semid); free_1chopstick(right,semid); #endif } } int main(int argc,char *argv[]){ int semid; //创建信号量 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);//semctl()系统调用在一个信号量集或集合中的单个信号量上执行各种控制操作 } //创建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);//num 代表进程号 return 0; }
4. 运行结果并解释
分析:一共有五位哲学家,一开始五位哲学家都在思考,过了一段时间哲学家饿了,如果两边筷子都可以使用,就可以进餐,如果有一边的筷子正在被使用则该哲学家需要等待无法就餐,所以相邻的两位哲学家不能同时用餐。