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

  • 姓名:林顺达
  • 学号:201821121022
  • 班级:计算1811

1. 哲学家进餐问题

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

2. 给出伪代码

伪代码:

void philosopher(哲学家编号,从0到4)
{
    while(真)
    {
        哲学家思考;
        饿了,拿起左叉子,更改左边信号量;
        拿起右叉子,更改右边信号量;
        进食;
        放下左叉子;
        放下右叉子;
    }
}

这样可以保证不会有相邻的哲学家同时进食,但当五位哲学家都拿起左叉子时会引起死锁,由于五个叉子的信号量为0,但同时又等待右叉子,而又无空余的叉子,此时就进入了无限等待状态

对于上述死锁解决方法:同时拿起左右叉子,并且同时放下,伪代码:

void philosopher(哲学家编号,从0到4)
{
    while(真)
    {
        哲学家思考;
        饿了,同时拿起左右叉子,同时更改左右信号量
        进食;
        同时放下左右叉子,同时更改左右信号量
    }
}

3. 给出完整代码

#include<stdio.h>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<errno.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/wait.h>
#define ERR_EXIT(m) \
    do { \
        perror(m); \
        exit(EXIT_FAILURE); \
    } while(0)

#define SEMS_NUM 5  //信号量数目 
#define THINKER_NUM 5 //哲学家人数 
#define FORK_NUM 5 //叉子数 
#define TIME_WAIT (rand() % 5 + 1) //等待时间 

union semun
{
    int val;    //SETVAL用的值
    struct semid_ds *buf;  //IPC_STAT、IPC_SET用的semid_ds结构
    unsigned short  *array; //SETALL、GETALL用的数组值
    struct seminfo  *__buf; //为控制IPC_INFO提供的缓存
};

/*对信号量数组编号为no的信号量做P操作
  哲学家拿取两把叉子,如果未拿到则进餐受阻 
*/
void P(int sem_id,int left_fno,int right_fno)
{     
    /*
         sbuf.sem_num:序号
         sbuf.sem_op:操作,-1表示P操作 
         sem_flg:设置信号量的操作 
    */
     struct sembuf sbuf[2] =
     {
        {left_fno, -1, SEM_UNDO},
        {right_fno, -1, SEM_UNDO}
     };
     
     if(semop(sem_id, sbuf, 2) == -1) {
         ERR_EXIT("P");
     }
}
/*
对信号量数组编号为no的信号量做V操作 哲学家放回叉子 */ void V(int sem_id,int left_fno,int right_fno){ /* sbuf.sem_num:序号 sbuf.sem_op:操作,1表示V操作 sem_flg:设置信号量的操作 */ struct sembuf sbuf[2] = { {left_fno, 1, SEM_UNDO}, {right_fno, 1, SEM_UNDO} }; if(semop(sem_id, sbuf, 2) == -1) { ERR_EXIT("V"); } } void think(int tk_no){ printf("philosopere%d is thinking\n", tk_no); sleep(TIME_WAIT); } void eat(int tk_no){ printf("philosopere%d is eating\n", tk_no); sleep(TIME_WAIT); } void hungry(int tk_no){ printf("philosopere%d is hungry,need eat\n", tk_no); } void philosopere(int sem_id,int tk_no) { srand(getpid()); int left_fno=tk_no; //left_fno左边叉子编号 int right_fno=(left_fno+1)%FORK_NUM; while(1) { think(tk_no);//思考 hungry(tk_no);//饥饿 P(sem_id,left_fno,right_fno);//拿取一双叉子 eat(tk_no);//就餐 V(sem_id,left_fno,right_fno);//放下叉子 } } int main() { /*
1.创建 Semaphore(计数信号量) 创建信号集,其中有SEMS_NUM个信号量
*/ int sem_id = semget(IPC_PRIVATE, SEMS_NUM, IPC_CREAT | 0666); if (sem_id == -1){ ERR_EXIT("semget"); } //2.初始化 Semaphore union semun sem; sem.val = 1; int i; for (i = 0; i < SEMS_NUM; i++){ if(semctl(sem_id, i, SETVAL, sem)==-1){ ERR_EXIT("semctl"); } } int tk_no = 0;//哲学家编号 pid_t pid; //创建子进程 for (i = 1; i < THINKER_NUM; i++) { pid = fork(); if (pid == -1){ ERR_EXIT("fork"); } if (pid == 0){ tk_no = i; break; } } philosopere(sem_id,tk_no);//处理就餐 return 0; }

4. 运行结果并解释

 

 结果解释:

  共有5个哲学家,对应着5个进程,其中针对上述死锁问题的改进,程序一开始为4、2(不相临的)拿起筷子并且进食,后来吃完后4进入思考状态,接着3、4处于饥饿状态需要进食,由于此时2还在eating,故3需要等待,而4继续进食,接下来的事件均以此类推

posted @ 2020-05-30 23:38  丶Lin  阅读(148)  评论(0编辑  收藏  举报