操作系统第6次实验报告:使用信号量解决进程互斥访问
- 姓名:徐思婕
- 学号:201821121004
- 班级:计算1811
1. 哲学家进餐问题
有五个哲学家,他们的生活方式是交替地进行思考和进餐。他们共用一张圆桌,分别坐在五张椅子上。在圆桌上有五个碗和五支筷子,平时一个哲学家进行思考,饥饿时便试图取用其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐。进餐完毕,放下筷子又继续思考。
2. 给出伪代码
放在桌子上的筷子在一段时间内只允许一位哲学家使用, 为了实现对筷子的互斥访问,5支筷子分别设置为为初始值为1的互斥信号量:
semaphore chopstick[5] = {1,1,1,1,1}; while(true) { //当哲学家饥饿时,总是先拿左边的筷子,再拿右边的筷 wait(chopstick[i]); wait(chopstick[(i+1)%5]); //就餐 //当哲学家进餐完成后,总是先放下左边的筷子,再放下右边的筷子 signal(chopstick[i]); signal(chopstick[(i+1)%5]); }
若五位哲学家同时饥饿而各自拿起了左边的筷子,这使五个信号量 chopstick 均为 0,当他们试图去拿起右边的筷子时,都将因无筷子而无限期地等待下去,即可能会引起死锁。
为避免死锁,可以使用AND型信号量实现:
semaphore chopstick[5]={1,1,1,1,1}; do{ //思考 Swait(chopstick[(i+1)%5],chopstick[i]); //就餐 Ssignal(chopstick[(i+1)%5],chopstick[i]); }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) //相当于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; //筷子值是两个 //第一个参数是编号 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 left = no; 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. 运行结果并解释
- 运行结果:
- 结果分析:一共有0-4五位哲学家,一开始五位哲学家都在思考,一段时间后哲学家饿了,若是两边筷子都可以使用,才能够进餐,只要有一边的筷子正在被使用则该哲学家需要等待无法就餐,即相邻的两位哲学家不能同时用餐。