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

姓名:傅伟杰

班级:计算1811

学号:201821121018

1. 选择哪一个问题

我选择的是哲学家进餐问题

首先对于哲学家进餐问题进行简述:

哲学家进餐问题描述有五个哲学家,他们的生活方式是交替地进行思考和进餐,n哲学家们共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五支筷子,n平时哲学家进行思考,饥饿时便试图取其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐,n进餐完毕,放下筷子又继续思考。

场景如下:

 

 

 

2. 给出伪代码

首先需要声明的是哲学家编号为0-4,同理筷子的编号也是0-4,那么对于1哲学家来说筷子1即为哲学家1左边的筷子

首先创建我们的5个信号量,信号量表示这5根筷子,初始化默认值为1,代表着这个筷子空闲,可以被使用,然后我们通过进程,创建了4个子进程,用来安排我们的5个哲学家的进餐问题,编号由0到4。

 下面我们解释哲学家进餐philosopher函数,我们设置所以的哲学家一开始都是处于思考状态,然后随之开始产生饥饿,接着哲学家开始确定是否拿起筷子的问题,由于吃饭时一定要2只筷子的,如果我们采用了每个人先拿起左边筷子,然后等待右边筷子,这就会出现我们常说的死锁问题,所以为了解决这个问题,我采用了让哲学家同时拿起左右的筷子,吃完之后,同时放下筷子,这样就保证了最多有2个哲学家同时吃饭,并且不会相邻,也就是死锁问题的解决。

void philosopher(unsigned short int i)
{
    while(1)
    {
        think();//思考
        take_chopstk(i,(i+1)%5);//拿起筷子
        eat();//吃饭
        free_chopstk(i,(i+1)%5);//放下筷子
    }
}

3. 给出完整代码

源代码如下:

#include<stdlib.h>
#include<string.h>
#include<stdint.h>
#include<stdbool.h>
#include<errno.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/wait.h>
#include<stdio.h>
#define error_exit(m) \
        do{ \
                perror(m); \
                exit (EXIT_FAILURE); \
        }while(0)
//错误退出
#define DELAY (rand() % 5 + 1)
union semun
{
        int val;
        struct semid_ds *buf;
        unsigned short * array;
        struct seminfo *__buf;
};

//确保有2个筷子,执行P操作
void sure_2chopstk(int i,int semid)
{
        int lchopstk = i;
        int rchopstk = (i+1) % 5;
        struct sembuf buf[2] = {
                {lchopstk,-1,0},{rchopstk,-1,0}
        };
        semop(semid,buf,2);

}
//放下2个筷子,执行V操作
void free_2chopstk(int i,int semid)
{
        int lchopstk = i;
        int rchopstk = (i+1) % 5;
        struct sembuf buf[2] = {
                {lchopstk,1,0},{rchopstk,1,0}
        };
        semop(semid,buf,2);
}
//思考
void think(int i)
{
        printf("philosoper %d is thinking\n",i);
        sleep(DELAY);
}
//吃饭
void eat(int i)
{
        printf("philosoper %d is eating\n",i);
        sleep(DELAY);
}
//饿了
void hungry(int i)
{
        printf("philosoper %d is hungry, need to eat\n",i);
}
///*各个哲学家吃饭方式的定义
 1.思考
 2.饿了,拿起左边的筷子(i)
 3.拿起右边的筷子(i+1)%5
 4.吃饭
 5.放下左边筷子
 6.放下右边筷子*/
void philosopher(int i,int semid)
{
        srand(getpid());
        int lchopstk = i;
        int rchopstk = (i+1) % 5;
        while(1)
        {
                think(i);
                hungry(i);
                sure_2chopstk(i,semid);
                eat(i);
                free_2chopstk(i,semid);
        }
}

int main(int argc,char *argv[])
{
        int semid;
        semid = semget(IPC_PRIVATE,5,IPC_CREAT | 0666);
        //定义5个信号量
        if(semid < 0)
        {
                error_exit("semid");
        }
        union semun s;
        s.val = 1;
        int i;
        for(i=0;i<5;i++)
        {
                semctl(semid,i,SETVAL,s);
        }
        int num = 0;
        pid_t pid;//创建4个进程
        for(i=1;i<5;i++)
        {
                pid = fork();
                if(pid < 0)
                {
                        error_exit("fork");
                }
                if(pid == 0)
                {
                        num = i;
                        break;
                }
        }
        philosopher(num,semid);//执行主要函数代码
        return 0;

}

4. 运行结果并解释

 

 由于死锁问题被解决了,运行结果也就会无限性继续下去,上面截取了一部分运行结果。

下面进行结果分析

首先有这5个进程(1个父进程+4个子进程)也就是我们的5位哲学家,编号是0-4。一开始所有的哲学家都进行思考,所以输出了5条思考,接着开始哲学家产生饥饿感,需要进行进食,那么就要同时拿起左右两边的筷子开始吃饭,因此这里的哲学家只存在2种情况,同时拿到2只筷子的,和一只筷子都没拿到的,前者就可以进行用餐,然后吃完,放下筷子,后者就需要进行等待,等到左右两边筷子的状态都可以被拿起,才能开始进食,所以最后每一次输出2个哲学家同时在进食,并别塔曼的位置是不相邻的。

posted @ 2020-05-30 01:25  Vijay1  阅读(126)  评论(0编辑  收藏  举报