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

  • 姓名:吕煜华
  • 学号:201821121046
  • 班级:计算1812

1. 选择哪一个问题

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

2. 给出伪代码

使用一个信号量表示一只筷子,由这五个信号量构成信号量数组,放在圆桌上的筷子是临界资源,在一段时间内只允许一位哲学家的使用。为了实现对筷子的互斥访问,5支筷子分别设置为为初始值为1的互斥信号量:

semaphore chopstick[5]={1,1,1,1,1};
while(true)
{
    //当哲学家饥饿时,总是先拿左边的筷子,再拿右边的筷子
    wait(chopstick[i]);
    wait(chopstick[(i+1)%5]);
    //就餐
    //当哲学家进餐完成后,总是先放下左边的筷子,再放下右边的筷子
    signal(chopstick[i]);
    signal(chopstick[(i+1)%5]);
}

为解决死锁的问题,可以使用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. 给出完整代码

给出完整代码,适当添加注释。注意代码的可读性、可维护性。

  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 union semun{
 14     int val;
 15     struct semid_ds *buf;
 16     unsigned short *array;
 17     struct seminfo *_buf;
 18 };
 19 #define ERR_EXIT(m)\
 20 do {\
 21     perror(m);\
 22     exit(EXIT_FAILURE);\
 23 }while(0)
 24 /*相当于p操作,申请一个资源*/
 25 int wait_1chopstick(int no, int semid)
 26 {
 27     struct sembuf sb={ no, -1, 0};
 28     int ret = semop( semid, &sb, 1);
 29     //semop()系统调用在semid标识的信号量集中的信号量上执行一个或多个up或down    操作,可用于进程间的同步和互斥
 30     if(ret<0){
 31         ERR_EXIT("semop");
 32     }
 33     return ret;
 34 }
 35 /*相当于v操作,释放一个资源*/
 36 int free_1chopstick(int no, int semid)
 37 {
 38     struct sembuf sb ={no, 1, 0};
 39     int ret;
 40     ret = semop(semid, &sb, 1);
 41     if(ret<0){
 42         ERR_EXIT("semop");
 43     }
 44     return ret;
 45 }
 46 #define DELAY (rand()%5+1)   //筷子是一个临界资源
 47 /*相当于p操作*/
 48 void wait_for_2chopstick(int no, int semid)
 49 {
 50     int left = no;
 51     int right = (no+1)%5;
 52
 53     struct sembuf buf[2]={
 54         {left, -1, 0},
 55         {right, -1, 0}
 56     };    //左右两边筷子都可用才可进餐
 57     semop(semid, buf, 2);
 58 }
 59 /*相当于v操作*/
 60 void free_2chopstick(int no, int semid)
 61 {
 62     int left = no;
 63     int right = (no+1)%5;
 64         struct sembuf buf[2] = {
 65             {left, 1, 0},
 66             {right, 1, 0}
 67         };
 68         semop(semid, buf, 2);
 69 }
 70 void philosophere(int no, int semid)
 71 {
 72     srand(getpid());
 73     for(;;){
 74         #if 1
 75         printf("哲学家%d 正在思考\n", no);
 76         sleep(DELAY);
 77         printf("哲学家%d 饿了\n", no);
 78         wait_for_2chopstick(no, semid);
 79         printf("哲学家%d 正在进餐\n", no);
 80         sleep(DELAY);
 81         free_2chopstick(no, semid);
 82         #else
 83         //可能会造成死锁
 84         int left = no;
 85         int right = (no+1)%5;
 86         printf("哲学家%d 正在思考\n", no);
 87         sleep(DELAY);
 88         printf("哲学家%d 饿了\n", no);
 89         wait_1chopstick(left, semid);
 90         sleep(DELAY);
 91         wait_1chopstick(right,semid);
 92         printf("哲学家%d 正在进餐\n", no);
 93         sleep(DELAY);
 94         free_1chopstick(left, semid);
 95         free_1chopstick(right, semid);
 96         #endif
 97     }
 98 }
 99 int main(int argc, char*argv[]){
100     int semid;  //创建信号量
101     semid = semget(IPC_PRIVATE, 5 , IPC_CREAT | 0666);
102     if(semid<0){
103        ERR_EXIT("semid");
104     }
105     union semun su;
106     su.val = 1;
107     int i;
108     for(i = 0; i<5; i++){
109         semctl(semid, i, SETVAL,su);
110     }
111     //创建4个子进程
112     int num=0;
113     pid_t pid;
114     for(i=0; i<5; i++){
115         pid=fork();
116         if(pid<0){
117             ERR_EXIT("fork");
118         }
119         if(0 == pid){
120             num= i;
121             break;
122         }
123     }
124     philosophere(num, semid); //num代表进程号
125     return 0;
126 }

 

4. 运行结果并解释

给出运行结果截图,并解释结果。

 

 解释:

一开始五个哲学家都在思考,

然后第一个哲学家(编号为0)饿了,此时两边的筷子都可用所以哲学家0可以进餐

第五个哲学家(编号为4)饿了,但此时他右边的筷子被哲学家0占用了,不能进餐

第四个哲学家(编号为3)饿了,此时两边的筷子都可用所以哲学家3可以进餐

第三个哲学家(编号为2)饿了,但此时他右边的筷子被哲学家3占用了,不能进餐

然后第四个哲学家(编号为3)进餐完毕,正在思考,此时哲学家2可以进餐

posted @ 2020-05-31 16:49  Lvyuhua  阅读(373)  评论(0编辑  收藏  举报