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

  • 郑楚杭
  • 201821121009
  • 计算1811

1. 选择哪一个问题

选题为哲学家就餐问题

2. 给出伪代码

算法思想:

philosopher代表一个哲学家的活动,将其创建为五个不同的线程代表五位不同的哲学家。每位哲学家先思考(伪代码中的think)

,当某位哲学家饥饿的时候(伪代码中的hungry),就拿起他左边的筷子,然后拿起他右边的筷子,然后进餐,然后放下他左

右的筷子并进行思考。因为筷子是临界资源,所以当一位哲学家拿起他左右的筷子的时候,就会对他左右的筷子进行加锁,使

其他的哲学家不能使用,当该哲学家进餐完毕后,放下了筷子,才对资源解锁,从而使其他的哲学家可以使用。

void philosopher (void* arg) {
    while (1) {
        think();          //思考
        hungry();          //饥饿
        P(left);  //拿起左筷子并更改信号量
        P(right);  //拿起右筷子并更改信号量
        eat();            //进餐
        V(left);    //放下左筷子
        V(right);  //放下右筷子
    }
}

 

存在的死锁问题:

由于每个线程都等待其他线程释放资源从而被唤醒,从而每个线程陷入了无限等待的状态。在哲学家就餐问题中,

假设一开始每位哲学家都拿起其左边的筷子,然后每位哲学家又都尝试去拿起其右边的筷子,这个时候由于每根

筷子都已经被占用,因此每位哲学家都不能拿起其右边的筷子,只能等待筷子被其他哲学家释放。由此五个线程

进入了无限等待的状态,因此就陷入了死锁。

死锁问题的解决:

为了解决死锁问题,可以规定奇数号哲学家先拿起他左边的筷子,然后再去拿他右边的筷子,而偶数号的哲学家

则相反,这样的话总能保证一个哲学家能获得两根筷子完成进餐,从而释放其所占用的资源,伪代码如下:

void philosopher (void* arg) {
    int left = i;
    int right = (i + 1) % N;
    while (1) {
        think();          //思考
        hungry();
        if (i % 2 == 0) {//偶数哲学家,先右后左          
            P(right);
            P(left);
            eat();            //进餐
            V(left);    //放下左筷子
            V(right);  //放下右筷子
        }
        else {
            P(left]);
            P(right);
            eat(); 
            P(right);
            P(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)
{
    struct sembuf sbuf;
    sbuf.sem_num = phi_no; /*序号*/
    sbuf.sem_op = -1; /*P操作*/
    sbuf.sem_flg = SEM_UNDO;

    semop(sem_id, &sbuf, 1);
}


void V(unsigned short int phi_no)
{
    struct sembuf sbuf;
    sbuf.sem_num = phi_no; /*序号*/
    sbuf.sem_op = 1;  /*V操作*/
    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);
            /*printf("哲学家%d拿起了右手边筷子\n", phi_no);*/
            P(left);
            /*printf("哲学家%d拿起了左手边筷子\n", phi_no);*/
            printf("哲学家%d正在就餐\n", phi_no);
            V(left);
            /*printf("哲学家%d放下了左手边筷子\n", phi_no);*/
            V(right);
            /*printf("哲学家%d放下了右手边筷子\n", phi_no);*/
        }
        else {                    //奇数先左后右 
            P(left);
            /*printf("哲学家%d拿起了左手边筷子\n", phi_no);*/
            P(right);
            /*printf("哲学家%d拿起了右手边筷子\n", phi_no);*/
            printf("哲学家%d正在就餐\n", phi_no);
            V(right);
            /*printf("哲学家%d放下了右手边筷子\n", phi_no);*/
            V(left);
            /*printf("哲学家%d放下了左手边筷子\n", phi_no);*/
        }
    } 
}

int main() {
    // Step 1: create semaphore
    // Generate a key
    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);
    }


    // Step 2: Init semaphore
    union semun arg;
    arg.val = 1;

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

    // Step 3: Manage dining
    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-4的五个哲学家,程序开始时哲学家都处于思考状态,当哲学家饿了需要进餐,奇数号的哲学家先拿

左筷子再拿右筷子,偶数好的哲学家相反,当哲学家拿到左右手的筷子以后开始进餐并且保证了每个哲学家都拿到两支筷子。

5. 参考资料

https://blog.csdn.net/Yun_Ge/article/details/89177918

posted on 2020-05-28 14:50  EzforNiko+  阅读(288)  评论(0编辑  收藏  举报

导航