操作系统第六次实验报告——使用信号量解决哲学家进餐问题
使用信号量解决哲学家进餐问题
0 个人信息
- 张樱姿
- 201821121038
- 计算1812
1 实验目的
- 通过编程进一步了解信号量。
2 实验内容
- 在服务器上用Vim编写一个程序:使用信号量解决任一个经典PV问题,测试给出结果,并对运行结果进行解释。
3 实验报告
3.1 选择哲学家进餐问题
五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五只筷子,他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐毕,放下筷子继续思考。
3.2 伪代码
分析:放在圆桌上的筷子是临界资源,在一段时间内只允许一位哲学家的使用。为了实现对筷子的互斥使用,使用一个信号量表示一只筷子,由这五个信号量构成信号量数组:
semaphore chopstick[5]={1,1,1,1,1};
所有信号量均被初始化为1,则第i位哲学家的活动可描述为:
do{ /*当哲学家饥饿时,总是先拿起左边的筷子,再拿起右边的筷子*/ wait(chopstick[i]); //拿起左筷子 wait(chopstick[(i+1)%5]); //拿起右筷子 eat(); //进餐 /*当哲学家进餐完成后,总是先放下左边的筷子,再放下右边的筷子*/ signal(chopstick[i]); //放下左筷子 signal(chopstick[i+1]%5); //放下右筷子 think(); //思考 }while(true);
但是在上述情况中,假如五位哲学家同时饥饿而各自拿起左边的筷子时,就会使五个信号量chopstick均为0;而当他们再试图去拿右边的筷子时,都将因无筷子可拿造成无限的等待,产生死锁。
因此,需要对上述算法进行改进,限制仅当哲学家左右的两只筷子都可用时,才允许他拿起筷子进餐。可以利用AND 型信号量机制实现,也可以利用信号量的保护机制实现。利用信号量的保护机制实现的思想是通过记录型信号量mutex对取左侧和右侧筷子的操作进行保护,使之成为一个原子操作,这样可以防止死锁的出现。利用AND型信号量机制可获得最简洁的解法。
①使用记录信号量实现:
semaphore mutex = 1; // 这个过程需要判断两根筷子是否可用,并保护起来semaphore chopstick[5]={1,1,1,1,1};void philosopher(int i){ while(true) { /* 这个过程中可能只能由一个人在吃饭,效率低下,有五只筷子,其实是可以达到两个人同时吃饭 */ think(); //思考 wait(mutex); //保护信号量 wait(chopstick[(i+1)%5]); //请求右边的筷子 wait(chopstick[i]); //请求左边的筷子 signal(mutex); //释放保护信号量 eat(); //进餐 signal(chopstick[(i+1)%5]); //释放右手边的筷子 signal(chopstick[i]); //释放左手边的筷子 }}
②使用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.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 14 15 union semun 16 { 17 int val; 18 struct semid_ds *buf; 19 unsigned short *array; 20 struct seminfo *__buf; 21 }; 22 23 #define ERR_EXIT(m) \ 24 do { \ 25 perror(m); \ 26 exit(EXIT_FAILURE); \ 27 } while(0) 28 29 //申请一个资源 30 int wait_1chop(int no,int semid) 31 { 32 //资源减1 33 struct sembuf sb = {no,-1,0}; 34 int ret; 35 ret = semop(semid,&sb,1); 36 if(ret < 0) { 37 ERR_EXIT("semop"); 38 } 39 return ret; 40 } 41 42 // 释放一个资源 43 int free_1chop(int no,int semid) 44 { 45 //资源加1 46 struct sembuf sb = {no,1,0}; 47 int ret; 48 ret = semop(semid,&sb,1); 49 if(ret < 0) { 50 ERR_EXIT("semop"); 51 } 52 return ret; 53 } 54 55 //筷子是一个临界资源 56 #define DELAY (rand() % 5 + 1) 57 58 //相当于P操作 59 //第一个参数是筷子编号 60 //第二个参数是信号量编号 61 void wait_for_2chop(int no,int semid) 62 { 63 //哲学家左边的筷子编号和哲学家编号是一样的 64 int left = no; 65 //右边的筷子 66 int right = (no + 1) % 5; 67 68 //筷子值是两个 69 //操作的是两个信号量,即两种资源都满足,才进行操作 70 struct sembuf buf[2] = { 71 {left,-1,0}, 72 {right,-1,0} 73 }; 74 //信号集中有5个信号量,只是对其中的资源sembuf进行操作 75 semop(semid,buf,2); 76 } 77 78 //相当于V操作 ,释放筷子 79 void free_2chop(int no,int semid) 80 { 81 int left = no; 82 int right = (no + 1) % 5; 83 struct sembuf buf[2] = { 84 {left,1,0}, 85 {right,1,0} 86 }; 87 semop(semid,buf,2); 88 } 89 90 91 //哲学家要做的事 92 void philosophere(int no,int semid) 93 { 94 srand(getpid()); 95 for(;;) 96 { 97 #if 1 98 //当两只筷子都可用的时候,哲学家才能进餐 99 printf("%d is thinking\n",no); //思考中100 sleep(DELAY);101 printf("%d is hungry\n",no); //饥饿102 wait_for_2chop(no,semid); //拿到两只筷子才能吃饭103 printf("%d is eating\n",no); //进餐104 sleep(DELAY);105 free_2chop(no,semid); //释放两只筷子106 #else107 //可能会造成死锁108 int left = no;109 int right = (no + 1) % 5;110 printf("%d is thinking\n",no); //思考中111 sleep(DELAY); 112 printf("%d is hungry\n",no); //饥饿113 wait_1chop(left,semid); //拿起左筷子(只要有一个资源就申请)114 sleep(DELAY); 115 wait_1chop(right,semid); //拿起右筷子116 printf("%d is eating\n",no); //进餐117 sleep(DELAY);118 free_1chop(left,semid); //释放左筷子119 free_1chop(right,semid); //释放右筷子120
No comments:
Post a Comment