操作系统第6次实验报告:使用信号量解决进程互斥访问
- 姓名:吕煜华
- 学号:201821121046
- 班级:计算1812
1. 选择哪一个问题
- 哲学家进餐问题
有五个哲学家,他们的生活方式是交替地进行思考和进餐,哲学家们共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五支筷子,平时哲学家进行思考,饥饿时便试图取其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐,该哲学家进餐完毕后,放下左右两只筷子又继续思考。
约束条件:
(1)只有拿到两只筷子时,哲学家才能吃饭。
(2)如果筷子已被别人拿走,则必须等别人吃完之后才能拿到筷子。
(3)任一哲学家在自己未拿到两只筷子吃完饭前,不会放下手中已经拿到的筷子。
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]); }
为解决死锁的问题,可以使用AND型信号量实现:仅当一个哲学家左右两边的筷子都可用时才允许他进餐。
semaphore chopstick[5]={1,1,1,1,1}; do{ think(); //思考 Swait(chopstick[(i+1)%5],chopstick[i]); //请求筷子 eat(); //就餐 Ssignal(chopstick[(i+1)%5],chopstick[i]); //释放筷子 }while(true);
3. 给出完整代码
给出完整代码,适当添加注释。注意代码的可读性、可维护性。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <stdint.h> 5 #include <stdbool.h> 6 #include <errno.h> 7 #include <unistd.h> 8 #include <sys/types.h> 9 #include <sys/stat.h> 10 #include <sys/ipc.h> 11 #include <sys/sem.h> 12 #include <sys/wait.h> 13 union semun{ 14 int val; 15 struct semid_ds *buf; 16 unsigned short *array; 17 struct seminfo *_buf; 18 }; 19 #define ERR_EXIT(m)\ 20 do {\ 21 perror(m);\ 22 exit(EXIT_FAILURE);\ 23 }while(0) 24 /*相当于p操作,申请一个资源*/ 25 int wait_1chopstick(int no, int semid) 26 { 27 struct sembuf sb={ no, -1, 0}; 28 int ret = semop( semid, &sb, 1); 29 //semop()系统调用在semid标识的信号量集中的信号量上执行一个或多个up或down 操作,可用于进程间的同步和互斥 30 if(ret<0){ 31 ERR_EXIT("semop"); 32 } 33 return ret; 34 } 35 /*相当于v操作,释放一个资源*/ 36 int free_1chopstick(int no, int semid) 37 { 38 struct sembuf sb ={no, 1, 0}; 39 int ret; 40 ret = semop(semid, &sb, 1); 41 if(ret<0){ 42 ERR_EXIT("semop"); 43 } 44 return ret; 45 } 46 #define DELAY (rand()%5+1) //筷子是一个临界资源 47 /*相当于p操作*/ 48 void wait_for_2chopstick(int no, int semid) 49 { 50 int left = no; 51 int right = (no+1)%5; 52 53 struct sembuf buf[2]={ 54 {left, -1, 0}, 55 {right, -1, 0} 56 }; //左右两边筷子都可用才可进餐 57 semop(semid, buf, 2); 58 } 59 /*相当于v操作*/ 60 void free_2chopstick(int no, int semid) 61 { 62 int left = no; 63 int right = (no+1)%5; 64 struct sembuf buf[2] = { 65 {left, 1, 0}, 66 {right, 1, 0} 67 }; 68 semop(semid, buf, 2); 69 } 70 void philosophere(int no, int semid) 71 { 72 srand(getpid()); 73 for(;;){ 74 #if 1 75 printf("哲学家%d 正在思考\n", no); 76 sleep(DELAY); 77 printf("哲学家%d 饿了\n", no); 78 wait_for_2chopstick(no, semid); 79 printf("哲学家%d 正在进餐\n", no); 80 sleep(DELAY); 81 free_2chopstick(no, semid); 82 #else 83 //可能会造成死锁 84 int left = no; 85 int right = (no+1)%5; 86 printf("哲学家%d 正在思考\n", no); 87 sleep(DELAY); 88 printf("哲学家%d 饿了\n", no); 89 wait_1chopstick(left, semid); 90 sleep(DELAY); 91 wait_1chopstick(right,semid); 92 printf("哲学家%d 正在进餐\n", no); 93 sleep(DELAY); 94 free_1chopstick(left, semid); 95 free_1chopstick(right, semid); 96 #endif 97 } 98 } 99 int main(int argc, char*argv[]){ 100 int semid; //创建信号量 101 semid = semget(IPC_PRIVATE, 5 , IPC_CREAT | 0666); 102 if(semid<0){ 103 ERR_EXIT("semid"); 104 } 105 union semun su; 106 su.val = 1; 107 int i; 108 for(i = 0; i<5; i++){ 109 semctl(semid, i, SETVAL,su); 110 } 111 //创建4个子进程 112 int num=0; 113 pid_t pid; 114 for(i=0; i<5; i++){ 115 pid=fork(); 116 if(pid<0){ 117 ERR_EXIT("fork"); 118 } 119 if(0 == pid){ 120 num= i; 121 break; 122 } 123 } 124 philosophere(num, semid); //num代表进程号 125 return 0; 126 }
4. 运行结果并解释
给出运行结果截图,并解释结果。
解释:
一开始五个哲学家都在思考,
然后第一个哲学家(编号为0)饿了,此时两边的筷子都可用所以哲学家0可以进餐
第五个哲学家(编号为4)饿了,但此时他右边的筷子被哲学家0占用了,不能进餐
第四个哲学家(编号为3)饿了,此时两边的筷子都可用所以哲学家3可以进餐
第三个哲学家(编号为2)饿了,但此时他右边的筷子被哲学家3占用了,不能进餐
然后第四个哲学家(编号为3)进餐完毕,正在思考,此时哲学家2可以进餐