操作系统第6次实验报告:使用信号量解决进程互斥访问
- 袁祎琦
- 201821121033
- 计算1812
1. 选择哪一个问题
我选择哲学家进餐问题。
2. 给出伪代码
分析:5名哲学家与左右邻居对其中间筷子的访问时互斥关系。本题的关键是如何让一个哲学家拿到左右两个筷子而不造成死锁或者饥饿现象。
一共有5个哲学家(编号0~4),5支筷子(编号也是0~4),0号哲学家右手的筷子是0号,筷子和哲学家编号都是逆时针增加,所以 :
0号哲学家对应的是:4号和1号筷子。
1号哲学家对应的是:0号和2号筷子。
2号哲学家对应的是:1号和3号筷子。
3号哲学家对应的是:2号和4号筷子。
4号哲学家对应的是:3号和0号筷子。
所以,
1 #define left(phi_id) (phi_id+N-1)%N 2 #define right(phi_id) (phi_id+1)%N 3 #define N 5 // five philosopher
5支筷子对应5个互斥锁。
仅当哲学家的左,右两只筷子均可用的时候,才允许他拿筷子进餐。
1 semaphore chopstick[5]={1,1,1,1,1}; 2 void philosopher(int i) 3 { 4 while(true) 5 { 6 think(); 7 if(i%2 == 0) //偶数哲学家,先右后左。 8 { 9 wait (chopstick[(i + 1)%5]) ; 10 wait (chopstick[i]) ; 11 eat(); 12 signal (chopstick[(i + 1)%5]) ; 13 signal (chopstick[i]) ; 14 } 15 else //奇数哲学家,先左后右。 16 { 17 wait (chopstick[i]) ; 18 wait (chopstick[(i + 1)%5]) ; 19 eat(); 20 signal (chopstick[i]) ; 21 signal (chopstick[(i + 1)%5]) ; 22 } 23 } 24 }
3. 给出完整代码
给出完整代码,适当添加注释。注意代码的可读性、可维护性。
1 /* 2 * every philosopher is in while loop: thinking -> take_forks -> eating -> put_down_forks -> thingking 3 * 4 * 规定奇数号的哲学家先拿起他左边的筷子,然后再去拿他右边的筷子; 5 * 而偶数号的哲学家则先拿起他右边的筷子,然后再去拿他左边的筷子。 6 * 7 */ 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <pthread.h> 11 #include <semaphore.h> 12 #include <unistd.h> 13 14 #define N 5 // five philosopher 15 #define T_EAT 5 //哲学家吃饭时间 16 #define T_THINK 5 //哲学家思考时间 17 #define N_ROOM 4 //同一时间只允许4人用餐 18 #define left(phi_id) (phi_id+N-1)%N 19 #define right(phi_id) (phi_id+1)%N 20 21 enum { think , hungry , eat }phi_state[N]; 22 sem_t chopstick[N]; 23 sem_t room; 24 25 void thinking(int id){ //思考 26 sleep(T_THINK); 27 printf("philosopher[%d] is thinking...\n", id); 28 } 29 30 void eating(int id){ //就餐 31 sleep(T_EAT); 32 printf("philosopher[%d] is eating...\n", id); 33 } 34 35 void take_forks(int id){ 36 //获取左右两边的筷子 37 printf("Pil[%d], left[%d], right[%d]\n", id, left(id), right(id)); 38 if((id&1) == 1){ 39 sem_wait(&chopstick[left(id)]); 40 sem_wait(&chopstick[right(id)]); 41 } 42 else{ 43 sem_wait(&chopstick[right(id)]); 44 sem_wait(&chopstick[left(id)]); 45 } 46 printf("philosopher[%d] take_forks...\n", id); 47 } 48 49 void put_down_forks(int id){ //放筷子 50 printf("philosopher[%d] is put_down_forks...\n", id); 51 sem_post(&chopstick[left(id)]); 52 sem_post(&chopstick[right(id)]); 53 } 54 55 void* philosopher_work(void *arg){ //哲学家就餐 56 int id = *(int*)arg; 57 printf("philosopher init [%d] \n", id); 58 while(1){ 59 thinking(id);//思考 60 sem_wait(&room); 61 take_forks(id);//拿筷子 62 sem_post(&room); 63 eating(id);//就餐 64 put_down_forks(id);//放筷子 65 } 66 } 67 68 int main(){ 69 pthread_t phiTid[N]; 70 int i; 71 int err; 72 int *id=(int *)malloc(sizeof(int)*N); 73 //初始化 74 for (i = 0; i < N; i++) 75 { 76 if(sem_init(&chopstick[i], 0, 1) != 0) 77 { 78 printf("init forks error\n"); 79 } 80 } 81 sem_init(&room, 0, N_ROOM); 82 for(i=0; i < N; ++i){ 83 id[i] = i; 84 err = pthread_create(&phiTid[i], NULL, philosopher_work, (void*)(&id[i])); //这种情况生成的thread id是0,1,2,3,4 85 if (err != 0) 86 printf("can't create process for reader\n"); 87 } 88 89 while(1); 90 91 // delete the source of semaphore 92 for (i = 0; i < N; i++) 93 { 94 err = sem_destroy(&chopstick[i]); 95 if (err != 0) 96 { 97 printf("can't destory semaphore\n"); 98 } 99 } 100 exit(0); 101 return 0; 102 }
4. 运行结果并解释
给出运行结果截图,并解释结果。
刚开始的时候,全部哲学家都在思考。
(0号哲学家对应的是: 4号和1号筷子
1号哲学家对应的是: 0号和2号筷子.
2号哲学家对应的是: 1号和3号筷子.
3号哲学家对应的是: 2号和4号筷子.
4号哲学家对应的是: 3号和0号筷子.)
原理:将是1,2号哲学家竞争1号筷子,3,4号哲学家竞争3号筷子.即五个哲学家都竞争奇数号筷子,获得后,再去竞争偶数号筷子,最后总会有一个哲学家能获得两支筷子而进餐。而申请不到的哲学家进入阻塞等待队列,根FIFO原则,则先申请的哲学家会较先可以吃饭,因此不会出现饿死的哲学家。
5. 加分项
选题为哲学家就餐问题,同一时间只允许4人用餐,最后总会有一个哲学家能获得两支筷子而进餐。而申请不到的哲学家进入 阻塞等待队列,先申请的哲学家会比较先吃饭,因此不会出现饿死的哲学家。
6. 遇到的问题
我发现代码在ubuntu系统的服务器上跑不了,后来才知道是不支持里面的某些库。什么sem_wait,sem_post,sem_init,pthread_create。
如下图:
后来改本地跑了,毕竟Windows也是一种操作系统。