操作系统第6次实验报告:使用信号量解决进程互斥访问
- 姓名:吴永锋
- 学号:201821121051
- 班级:计算1812
1. 选择哪一个问题
哲学家进餐问题:
有五个哲学家围坐在一张圆桌旁就餐,,圆桌上有五个碗和五只筷子,他们的生活方式就是交替地进行思考和进餐。平时每个哲学家独立思考问题,饥饿时便试图分别取其左右两侧的筷子,只有两只筷子都拿到后才能进餐;进餐完毕后应立即放下筷子,然后继续思考问题。
2. 给出伪代码
由问题描述可知,哲学家共享的五只筷子是临界资源,为实现筷子的互斥使用,可为每只筷子设置一个互斥信号量,初值为1,使用一个信号量数组来表示。为描述方便,对每个哲学家进行编号:0~4;五只筷子及其对应的信号量编号也是0~4,且与哲学家编号相同的筷子位于该哲学家左侧。一种简单的解决思路是每个哲学家都先取其左侧的筷子,成功后再取其右侧的筷子,取到两只筷子后就进餐;进餐完毕后再一次释放着两只筷子。
semaphore chop[5]; void main(){ chop[5]={1,1,1,1,1}; parbegin(Philosopher(i)(i=0...4)); } void Philosopher(i){ do{ thinking; wait(chop[i]); wait(chop[i+1]mod5); eating; signal(chop[i]); signal)chop[i+1]mod5); }while(1); }
如果五个哲学家同时成功取到左边的筷子,之后再取右边的筷子时都会失败。五个哲学家都等待右侧筷子而不能吃饭,且都不能释放已取到的左边的筷子,进入死锁状态。
为了避免死锁,我们规定:仅当哲学家左右两侧的筷子都可用时,才允许他一次性同时拿起两只筷子。
semaphore chop[5]; void main(){ chop[5]={1,1,1,1,1}; parbegin(Philosopher(i)(i=0...4)); } void Philosopher(i) { do { thinking; Swait(chopstick[(i+1)]mod5,chop[i]); eating; Ssignal(chopstick[(i+1)]mod5,chop[i]); }while(1); }
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 8 #include <unistd.h> 9 #include <sys/types.h> 10 #include <sys/stat.h> 11 #include <sys/ipc.h> 12 #include <sys/sem.h> 13 #include <sys/wait.h> 14 15 16 union semun 17 { 18 int val; 19 struct semid_ds *buf; 20 unsigned short *array; 21 struct seminfo *__buf; 22 }; 23 24 25 #define ERR_EXIT(m) \ 26 do { \ 27 perror(m); \ 28 exit(EXIT_FAILURE); \ 29 } while(0) 30 31 32 //申请一个资源 33 int wait_1fork(int no,int semid) 34 { 35 //int left = no; 36 //int right = (no + 1) % 5; 37 struct sembuf sb = {no,-1,0}; 38 int ret; 39 ret = semop(semid,&sb,1); 40 if(ret < 0) { 41 ERR_EXIT("semop"); 42 } 43 return ret; 44 } 45 46 // 释放一个资源 47 int free_1fork(int no,int semid) 48 { 49 struct sembuf sb = {no,1,0}; 50 int ret; 51 ret = semop(semid,&sb,1); 52 if(ret < 0) { 53 ERR_EXIT("semop"); 54 } 55 return ret; 56 } 57 58 //这里表明叉子是一个临界资源 59 60 #define DELAY (rand() % 5 + 1) 61 62 //相当于P操作 63 void wait_for_2fork(int no,int semid) 64 { 65 //哲学家左边的刀叉编号和哲学家是一样的 66 int left = no; 67 //右边的刀叉 68 int right = (no + 1) % 5; 69 70 //刀叉值是两个 71 //注意第一个参数是编号 72 //操作的是两个信号量,即两种资源都满足,才进行操作 73 struct sembuf buf[2] = { 74 {left,-1,0}, 75 {right,-1,0} 76 }; 77 //信号集中有5个信号量,只是对其中的资源sembuf进行操作 78 semop(semid,buf,2); 79 } 80 81 //相当于V操作 ,释放刀叉 82 void free_2fork(int no,int semid) 83 { 84 int left = no; 85 int right = (no + 1) % 5; 86 struct sembuf buf[2] = { 87 {left,1,0}, 88 {right,1,0} 89 }; 90 semop(semid,buf,2); 91 } 92 93 94 //哲学家要做的事 95 void philosophere(int no,int semid) 96 { 97 srand(getpid()); 98 //srand(time(NULL)); 99 for(;;) 100 { 101 #if 1 102 //这里采取的措施是当两把刀叉都可用的时候(即两种资源都满足的时候) 103 //哲学家才能吃饭,这样不相邻的哲学家就可吃上饭 104 printf("%d is thinking\n",no); // 思考中 105 sleep(DELAY); 106 printf("%d is hungry\n",no); // 感觉到饥饿 107 wait_for_2fork(no,semid);//拿到两把叉子才能吃饭 108 printf("%d is eating\n",no); // 吃饭 109 sleep(DELAY); 110 free_2fork(no,semid);//释放两把叉子 111 #else 112 //这段代码可能会造成死锁 113 int left = no; 114 int right = (no + 1) % 5; 115 printf("%d is thinking\n",no); // 思考中 116 sleep(DELAY); 117 printf("%d is hungry\n",no); // 感觉到饥饿 118 wait_1fork(left,semid); // 拿起左叉子,现在是只要有一个资源,就申请 119 sleep(DELAY); 120 wait_1fork(right,semid); // 拿到右叉子 121 printf("%d is eating\n",no); // 吃饭 122 sleep(DELAY); 123 free_1fork(left,semid); // 释放左叉子 124 free_1fork(right,semid); // 释放右叉子 125 #endif 126 } 127 } 128 129 130 int main(int argc,char *argv[]) 131 { 132 int semid; 133 //创建信号量 134 //信号量集中5个信号量 135 semid = semget(IPC_PRIVATE,5,IPC_CREAT | 0666); 136 if(semid < 0) { 137 ERR_EXIT("semid"); 138 } 139 union semun su; 140 su.val = 1; 141 int i; 142 for(i = 0;i < 5;++i) { 143 //注意第二个参数也是索引 144 semctl(semid,i,SETVAL,su); 145 } 146 //创建4个子进程 147 int num = 0; 148 pid_t pid; 149 for(i = 1;i < 5;++i) 150 { 151 pid = fork(); 152 if(pid < 0) 153 { 154 ERR_EXIT("fork"); 155 } 156 if(0 == pid) // 子进程 157 { 158 num = i; 159 break; 160 } 161 } 162 //这里就是哲学家要做的事情 163 philosophere(num,semid); 164 return 0; 165 }
4. 运行结果并解释
运行后,不会停止,说明不出现死锁。
选取部分结果分析:
五个哲学家的编号为0~4。
1-5行:一开始,所有哲学家都处于思考状态。
6-7行:0号哲学家饥饿,此时其左右两边的筷子均可用,于是0号哲学家开始进餐。
8行:1号哲学家饥饿,但由于0号哲学家拿走了其右手边的筷子并且没有释放,于是1号哲学家保持饥饿状态。
9行:4号哲学家饥饿,但由于0号哲学家拿走了其左手边的筷子并且没有释放,于是4号哲学家保持饥饿状态。
10-11行:3号哲学家饥饿,此时其左右两边的筷子均可用,于是3号哲学家开始进餐。
12行:2号哲学家饥饿,但由于3号哲学家拿走了其左手边的筷子并且没有释放,于是2号哲学家保持饥饿状态。
13行:0号哲学家进入思考,释放左右两边的筷子。
14行:1号哲学家右手边的筷子被0号哲学家释放,此时1号哲学家左右两边的筷子均可用,于是1号哲学家开始进餐。
15行:3号哲学家进入思考,释放左右两边的筷子。
16行:4号哲学家左手边的筷子被0号哲学家释放,右手边的筷子被3号哲学家释放,此时4号哲学家左右两边的筷子均可用,于是4号哲学家开始进餐。
……
从中可以发现同时最多只能有两个哲学家一起用餐,也不会出现相邻哲学家一起用餐的情况。
参考博客: