操作系统第6次实验报告:使用信号量解决进程互斥访问

  • 姓名:程开
  • 学号:201821121060
  • 班级:计算1812

1. 选择哪一个问题

  • 哲学家进餐问题

  哲学家进餐问题是由荷兰学者Dijkstra提出的经典的同步问题之一。

  有五个哲学家,他们的生活方式是交替地进行思考和进餐,哲学家们共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五支筷子,平时哲学家进行思考,饥饿时便试图取其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐,该哲学家进餐完毕后,放下左右两只筷子又继续思考。
  约束条件
  (1)只有拿到两只筷子时,哲学家才能吃饭。
  (2)如果筷子已被别人拿走,则必须等别人吃完之后才能拿到筷子。
  (3)任一哲学家在自己未拿到两只筷子吃完饭前,不会放下手中已经拿到的筷子。

2. 给出伪代码

 1 semaphore  chopstick chopstick[5] = {1,1,1,1,1};
 2 
 3 do
 4     {
 5         //think
 6         wait(chopstick[i]);
 7         wait(chopstick[(i+1)%5]);
 8         //eat
 9         signal(chopstick[i]);
10         signal(chopstick[(i+1)%5]);
11     }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  
  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 struct semid_ds {
 25     struct ipc_perm sem_perm;   /Ownership and permissions/
 26     time_t          sem_otime; /Last semop time/
 27     time_t          sem_ctime; /Last change time/
 28     unsigned long   sem_nsems; /No. of semaphores in set/
 29 };
 30 */
 31  
 32 //遇到错误,退出,"\"表示续行符 
 33 #define ERR_EXIT(m) \
 34     do { \
 35         perror(m); \
 36         exit(EXIT_FAILURE); \
 37     } while(0)
 38      
 39  
 40 //申请一个资源
 41 int    wait_1fork(int no,int semid)
 42 {
 43     //int left = no;
 44     //int right = (no + 1) % 5;
 45     /*struct sembuf{
 46     short sem_num;信号量的编号
 47     short sem_op;信号量一次PV操作时加减的数值
 48     short sem_flg;跟踪该信号量的修改情况
 49     }; */ 
 50     struct sembuf sb = {no,-1,0};
 51     int ret;
 52     ret = semop(semid,&sb,1);
 53     if(ret < 0) {
 54         ERR_EXIT("资源不足");
 55     }
 56     return ret;
 57 }
 58  
 59 // 释放一个资源
 60 int free_1fork(int no,int semid)
 61 {
 62     struct sembuf sb = {no,1,0};
 63     int ret;
 64     ret = semop(semid,&sb,1);
 65     if(ret < 0) {
 66         ERR_EXIT("资源释放错误");
 67     }
 68     return ret;
 69 }
 70  
 71 
 72  
 73 //相当于P操作
 74 void wait_for_2fork(int no,int semid)
 75 {
 76     //哲学家左边的刀叉编号和哲学家是一样的
 77     int left = no;
 78     //右边的刀叉
 79     int right = (no + 1) % 5;
 80  
 81     //刀叉值是两个
 82     //注意第一个参数是编号
 83     //操作的是两个信号量,即两种资源都满足,才进行操作
 84     struct sembuf buf[2] = {
 85         {left,-1,0},
 86         {right,-1,0}
 87     };
 88     //信号集中有5个信号量,只是对其中的资源sembuf进行操作
 89     semop(semid,buf,2);
 90 }
 91  
 92 //相当于V操作  ,释放刀叉
 93 void free_2fork(int no,int semid)
 94 {
 95     int left = no;
 96     int right = (no + 1) % 5;
 97     struct sembuf buf[2] = {
 98         {left,1,0},
 99         {right,1,0}
100     };
101     semop(semid,buf,2);
102 }
103  
104  
105 //哲学家要做的事
106 void philosophere(int no,int semid)
107 {
108     srand(getpid());
109     //srand(time(NULL));
110     for(;;) 
111     {
112         /*这里采取的措施是当两把刀叉都可用的时候(即两种资源都满足的时候)
113         //哲学家才能吃饭,这样不相邻的哲学家就可吃上饭
114         printf("哲学家%d正在思考\n",no);  // 思考中
115         sleep(3);
116         printf("哲学家%d饿了\n",no);  // 感觉到饥饿
117         wait_for_2fork(no,semid);//拿到两把叉子才能吃饭
118         printf("哲学家%d正在吃饭\n",no);  // 吃饭
119         sleep(3);
120         free_2fork(no,semid);//释放两把叉子*/
121     
122         //这段代码可能会造成死锁
123         int left = no;
124         int right = (no + 1) % 5;
125         printf("哲学家%d正在思考\n",no);  // 思考中
126         sleep(3); 
127         printf("哲学家%d饿了\n",no);   // 感觉到饥饿
128         wait_1fork(left,semid);    // 拿起左叉子,现在是只要有一个资源,就申请
129         sleep(3);            
130         wait_1fork(right,semid);   // 拿到右叉子
131         printf("哲学家%d正在吃饭\n",no);  // 吃饭
132         sleep(3);
133         free_1fork(left,semid); // 释放左叉子
134         free_1fork(right,semid);  // 释放右叉子
135     
136     }
137 }
138  
139  
140 int main(int argc,char *argv[])
141 {
142     int semid;
143     //创建信号量
144      //信号量集中5个信号量
145     semid = semget(IPC_PRIVATE,5,IPC_CREAT | 0666); 
146     if(semid < 0) {
147         ERR_EXIT("semid");
148     }
149     union semun su;
150     su.val = 1;
151     int i;
152     for(i = 0;i < 5;++i) {
153         //注意第二个参数也是索引
154         semctl(semid,i,SETVAL,su);
155     }
156     //创建4个子进程
157     int num = 0;
158     pid_t pid;
159     for(i = 1;i < 5;++i) 
160     {
161        pid = fork(); 
162        if(pid < 0) 
163         {
164            ERR_EXIT("fork");
165         }
166         if(0 == pid)  // 子进程
167         {
168             num = i;
169             break;
170         }
171     }
172     //哲学家开始 
173    philosophere(num,semid);
174     return 0;
175 }

 

4. 运行结果并解释

 

 

 

 

 

 5名哲学家同时拿起左筷子,出现死锁                                                            没有出现死锁时的正常状态

 

5. 加分项

要解决死锁问题,有如下几种方案

①增加一名服务员来管理就餐

②最多只允许4名哲学家同时拿起同一边的筷子,这样保证至少有一名哲学家完成进餐并释放资源

③使用AND信号量机制,如果某个哲学家想拿筷子,那他需要同时拿起左右筷子,然后进餐,否则就一个都不拿

④比较麻烦一点,规定奇数号哲学家先拿起他左边的筷子,然后再去拿他右边的筷子,而偶数号的哲学家则相反,这样的话总能保证一个哲学家能获得两根筷子完成进餐,从而释放其所占用的资源

这里采用方法③

//相当于P操作
void wait_for_2fork(int no,int semid)
{
    //哲学家左边的刀叉编号和哲学家是一样的
    int left = no;
    //右边的刀叉
    int right = (no + 1) % 5;
 
    //刀叉值是两个
    //注意第一个参数是编号
    //操作的是两个信号量,即两种资源都满足,才进行操作
    struct sembuf buf[2] = {
        {left,-1,0},
        {right,-1,0}
    };
    //信号集中有5个信号量,只是对其中的资源sembuf进行操作
    semop(semid,buf,2);
}

//相当于V操作  ,释放刀叉
void free_2fork(int no,int semid)
{
    int left = no;
    int right = (no + 1) % 5;
    struct sembuf buf[2] = {
        {left,1,0},
        {right,1,0}
    };
    semop(semid,buf,2);
}

  

或者是

void philosopher (void* arg) {
    while (1) {
        think();
        hungry();
        pthread_mutex_lock(mutex);
        pthread_mutex_lock(&chopsticks[left]);
        pthread_mutex_lock(&chopsticks[right]);
        pthread_mutex_unlock(mutex);
        eat();
        pthread_mutex_unlock(&chopsticks[left]);
        pthread_mutex_unlock(&chopsticks[right]);
    }
}

  

 

posted @ 2020-05-31 01:38  Amazing_C  阅读(216)  评论(0编辑  收藏  举报