操作系统第6次实验报告:使用信号量解决进程互斥访问
姓名:傅伟杰
班级:计算1811
学号:201821121018
1. 选择哪一个问题
我选择的是哲学家进餐问题
首先对于哲学家进餐问题进行简述:
哲学家进餐问题描述有五个哲学家,他们的生活方式是交替地进行思考和进餐,n哲学家们共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五支筷子,n平时哲学家进行思考,饥饿时便试图取其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐,n进餐完毕,放下筷子又继续思考。
场景如下:
2. 给出伪代码
首先需要声明的是哲学家编号为0-4,同理筷子的编号也是0-4,那么对于1哲学家来说筷子1即为哲学家1左边的筷子
首先创建我们的5个信号量,信号量表示这5根筷子,初始化默认值为1,代表着这个筷子空闲,可以被使用,然后我们通过进程,创建了4个子进程,用来安排我们的5个哲学家的进餐问题,编号由0到4。
下面我们解释哲学家进餐philosopher函数,我们设置所以的哲学家一开始都是处于思考状态,然后随之开始产生饥饿,接着哲学家开始确定是否拿起筷子的问题,由于吃饭时一定要2只筷子的,如果我们采用了每个人先拿起左边筷子,然后等待右边筷子,这就会出现我们常说的死锁问题,所以为了解决这个问题,我采用了让哲学家同时拿起左右的筷子,吃完之后,同时放下筷子,这样就保证了最多有2个哲学家同时吃饭,并且不会相邻,也就是死锁问题的解决。
void philosopher(unsigned short int i) { while(1) { think();//思考 take_chopstk(i,(i+1)%5);//拿起筷子 eat();//吃饭 free_chopstk(i,(i+1)%5);//放下筷子 } }
3. 给出完整代码
源代码如下:
#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> #include<stdio.h> #define error_exit(m) \ do{ \ perror(m); \ exit (EXIT_FAILURE); \ }while(0) //错误退出 #define DELAY (rand() % 5 + 1) union semun { int val; struct semid_ds *buf; unsigned short * array; struct seminfo *__buf; }; //确保有2个筷子,执行P操作 void sure_2chopstk(int i,int semid) { int lchopstk = i; int rchopstk = (i+1) % 5; struct sembuf buf[2] = { {lchopstk,-1,0},{rchopstk,-1,0} }; semop(semid,buf,2); } //放下2个筷子,执行V操作 void free_2chopstk(int i,int semid) { int lchopstk = i; int rchopstk = (i+1) % 5; struct sembuf buf[2] = { {lchopstk,1,0},{rchopstk,1,0} }; semop(semid,buf,2); } //思考 void think(int i) { printf("philosoper %d is thinking\n",i); sleep(DELAY); } //吃饭 void eat(int i) { printf("philosoper %d is eating\n",i); sleep(DELAY); } //饿了 void hungry(int i) { printf("philosoper %d is hungry, need to eat\n",i); } ///*各个哲学家吃饭方式的定义 1.思考 2.饿了,拿起左边的筷子(i) 3.拿起右边的筷子(i+1)%5 4.吃饭 5.放下左边筷子 6.放下右边筷子*/ void philosopher(int i,int semid) { srand(getpid()); int lchopstk = i; int rchopstk = (i+1) % 5; while(1) { think(i); hungry(i); sure_2chopstk(i,semid); eat(i); free_2chopstk(i,semid); } } int main(int argc,char *argv[]) { int semid; semid = semget(IPC_PRIVATE,5,IPC_CREAT | 0666); //定义5个信号量 if(semid < 0) { error_exit("semid"); } union semun s; s.val = 1; int i; for(i=0;i<5;i++) { semctl(semid,i,SETVAL,s); } int num = 0; pid_t pid;//创建4个进程 for(i=1;i<5;i++) { pid = fork(); if(pid < 0) { error_exit("fork"); } if(pid == 0) { num = i; break; } } philosopher(num,semid);//执行主要函数代码 return 0; }
4. 运行结果并解释
由于死锁问题被解决了,运行结果也就会无限性继续下去,上面截取了一部分运行结果。
下面进行结果分析
首先有这5个进程(1个父进程+4个子进程)也就是我们的5位哲学家,编号是0-4。一开始所有的哲学家都进行思考,所以输出了5条思考,接着开始哲学家产生饥饿感,需要进行进食,那么就要同时拿起左右两边的筷子开始吃饭,因此这里的哲学家只存在2种情况,同时拿到2只筷子的,和一只筷子都没拿到的,前者就可以进行用餐,然后吃完,放下筷子,后者就需要进行等待,等到左右两边筷子的状态都可以被拿起,才能开始进食,所以最后每一次输出2个哲学家同时在进食,并别塔曼的位置是不相邻的。