操作系统第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]={11111}; 
    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号哲学家开始进餐。

……

从中可以发现同时最多只能有两个哲学家一起用餐,也不会出现相邻哲学家一起用餐的情况。

参考博客:

https://blog.csdn.net/u014304293/article/details/46004677

https://blog.csdn.net/x1114832836/article/details/90670334

posted on 2020-05-29 21:27  矩小阵  阅读(307)  评论(0编辑  收藏  举报

导航