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

  • 姓名:倪晓东
  • 学号:201821121020
  • 班级:计算1811

1. 选择哪一个问题

  • 哲学家进餐问题

2. 给出伪代码

问题描述:

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

 

 

约束条件:

(1)只有拿到两只筷子时,哲学家才能吃饭。

(2)如果筷子已被别人拿走,则必须等别人吃完之后才能拿到筷子。

(3)任一哲学家在自己未拿到两只筷子吃完饭前,不会放下手中已经拿到的筷子。

主要矛盾:

因为一共只有5只筷子,不可能让所有哲学家同时进餐,筷子相对于临界资源,如果有一位哲学家在进餐,那么他的旁边两位哲学家就不可能也在进餐,如果5位哲学家同时拿起自己左手或者右手的筷子,那么就会因为缺少一只筷子进入等待状态,而又没有哲学家在进餐,所以不会有其他哲学家放下自己的筷子,就陷入一种死锁状态。

解决方法:

我们对哲学家进行编号,规定进餐时奇数哲学家依次拿起自己左边的筷子,偶数哲学家依次拿起自己右边的筷子,如果没有则进入等待状态,最后会有一个哲学家可以得到两只筷子可以进餐,该哲学家进餐结束后,释放两只筷子,会有另一位哲学家可以得到两只筷子进餐,依此类推。

void philosopher(unsigned short int phi_no) {
    int left = phi_no;            //记录左筷子编号
    int right = (phi_no + 1) % 5; //记录右筷子编号 
    while(1) {
        printf("哲学家%d正在思考问题\n", phi_no);
        printf("哲学家%d饿了\n", phi_no);
        if(phi_no % 2 == 0) {    //判断奇数偶数,偶数先右后左 
            P(right);            //哲学家拿起右手筷子 
            P(left);              //哲学家拿起左手筷子 
            printf("哲学家%d正在就餐\n", phi_no);//进餐结束后放下筷子
            V(left);             //哲学家放下左手筷子 
            V(right);             // 哲学家放下右手筷子
        }
        else {                    //奇数先左后右 
            P(left);               //哲学家拿起左手筷子 
            P(right);             //哲学家拿起右手筷子 
            printf("哲学家%d正在就餐\n", phi_no);//进餐结束后放下筷子
            V(right);              //哲学家放下右手筷子 
            V(left);               //哲学家放下左手筷子 
        }
    } 
}

 

 

3. 给出完整代码

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<time.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/types.h>
#include<unistd.h>
#include<pthread.h>
#include<semaphore.h>
#include<errno.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/wait.h>
#ifndef _SEMUN_H
#define _SEMUN_H
union semun {
        int val;                        /* value for SETVAL */
        struct semid_ds *buf;           /* buffer for IPC_STAT, IPC_SET */
        unsigned short *array;      /* array for GETALL, SETALL */
        struct seminfo *__buf;          /* buffer for IPC_INFO */
};
#endif

int sem_id; 
//拿起筷子 
void P(unsigned short int phi_no)//P操作,该筷子被人使用 
{
    struct sembuf sbuf;
    sbuf.sem_num = phi_no;     /*序号*/
    sbuf.sem_op = -1;         //对被使用的筷子进行标记 
    sbuf.sem_flg = SEM_UNDO;

    semop(sem_id, &sbuf, 1);
}

//放下筷子 
void V(unsigned short int phi_no)//V操作,筷子被人放下,无人使用 
{
    struct sembuf sbuf;
    sbuf.sem_num = phi_no;    /*序号*/
    sbuf.sem_op = 1;          //对未被使用的筷子进行标记 
    sbuf.sem_flg = SEM_UNDO;

    semop(sem_id, &sbuf, 1);
        
}

void philosopher(unsigned short int phi_no) {
    int left = phi_no;            //记录左筷子编号
    int right = (phi_no + 1) % 5; //记录右筷子编号 
    while(1) {
        printf("哲学家%d正在思考问题\n", phi_no);
        printf("哲学家%d饿了\n", phi_no);
        if(phi_no % 2 == 0) {    //判断奇数偶数,偶数先右后左 
            P(right);            //哲学家拿起右手筷子 
            P(left);              //哲学家拿起左手筷子 
            printf("哲学家%d正在就餐\n", phi_no);//进餐结束后放下筷子
            V(left);             //哲学家放下左手筷子 
            V(right);             // 哲学家放下右手筷子
        }
        else {                    //奇数先左后右 
            P(left);               //哲学家拿起左手筷子 
            P(right);             //哲学家拿起右手筷子 
            printf("哲学家%d正在就餐\n", phi_no);//进餐结束后放下筷子
            V(right);              //哲学家放下右手筷子 
            V(left);               //哲学家放下左手筷子 
        }
    } 
}

int main() {
    key_t key = ftok("/home", 1);
    if (key == -1){
        printf("ftok error.\n");
        exit(1);
    }

    // 创建信号量集,其中只有一个信号量
    sem_id = semget(key, 1, IPC_CREAT|0666);
    if(sem_id == -1) {
        printf("semget error.\n");
        exit(1);
    }

    union semun arg;
    arg.val = 1;

    if(semctl(sem_id, 0, SETVAL, arg) == -1) {
        printf("semctl error.\n");
        exit(1);
    }

    unsigned short int phi_no;    //number of philosopher
    pid_t pid;
    for (int i = 0; i < 5; i++){
        pid = fork();
        if (pid == -1) {
            printf("fork error.\n");
            exit(1);
        }
        if (pid == 0) {
            phi_no = i;
            break;
        }
    }
    
    philosopher(phi_no);
    return 0;
}

 

 

4. 运行结果并解释

对哲学家进行编号0,1,2,3,4。

 

当3,4号哲学家饿了,3号哲学家先拿起左手边筷子,3号哲学家左手边坐着4号哲学家,4号哲学家应该拿起自己右手边筷子,但是该筷子在3号哲学家手里,所以4号进入等待,3号可以继续拿起自己右手边筷子进餐。

 

 

4号哲学家进餐结束,放下筷子,3号饿了,可以拿起自己左手边筷子再拿右手边筷子,进行进餐,与1号哲学家没有资源冲突。

 5. 参考资料

https://baike.sogou.com/v70137267.htm?fromTitle=%E5%93%B2%E5%AD%A6%E5%AE%B6%E8%BF%9B%E9%A4%90%E9%97%AE%E9%A2%98

https://www.cnblogs.com/biglucky/p/4633706.html

 

posted @ 2020-05-31 14:27  Linial  阅读(201)  评论(0编辑  收藏  举报