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

  • 姓名:杨富宏
  • 学号:201821121017
  • 班级:计算1811

1. 哲学家进餐问题

   五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在桌子上有五只碗和五只筷子,他们的生活方式是交替地进行思考和进餐。

平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐毕,放下筷子继续思考。

2. 给出伪代码

void philosopher(int i)    // i:哲学家编号,从0到4
{
    while(TRUE)
    {
        think();                 // 哲学家思考
        take_fork(i);         //饿了,拿起左叉子
        take_fork((i+1)%N);  // 拿起右叉子
        eat();                     // 进食
        put_fork(i);          // 放下左叉子
        put_fork((i+1)%N);  // 放下右叉子
    }
}

可以保证不会有两个相邻的哲学家同时进餐,但却可能引起死锁的情况。假如五位哲学家同时饥饿而都拿起的左边的叉子,

就会使五个信号量fork都为0,当他们试图去拿右手边的叉子时,都将无叉子而陷入无限期的等待。因此出现了死锁问题。

解决:上面造成死锁是因为五个哲学家都拿起一边的叉子,而另一边的却没有拿到,继而一直等待着另外一边。

这样解决的办法就是哲学家必须同时拿起左右两边的叉子,这样可以保证每一次都有两个不相邻的哲学家同时进餐。

3. 给出完整代码

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<sys/ipc.h>
  4 #include<sys/msg.h>
  5 #include<sys/types.h>
  6 #include<unistd.h>
  7 #include<errno.h>
  8 #include<sys/ipc.h>
  9 #include<sys/sem.h>
 10 #include<sys/wait.h>
 11
 12 #define SEMS_NUM 5  //信号量数目
 13 #define THINKER_NUM 5 //哲学家人数
 14 #define FORK_NUM 5 //叉子数
 15 #define TIME_WAIT (rand() % 5 + 1) //等待时间
 16
 17 union semun
 18 {
 19 int val;    //SETVAL用的值
 20 struct semid_ds *buf;  //IPC_STAT、IPC_SET用的semid_ds结构
 21 unsigned short  *array; //SETALL、GETALL用的数组值
 22 struct seminfo  *__buf; //为控制IPC_INFO提供的缓存
 23 };
 24
 25 #define ERR_EXIT(m) \
 26 do { \
 27 perror(m); \
 28 exit(EXIT_FAILURE); \
 29 } while(0)
 30
 31 void P(int sem_id,int left_fno,int right_fno)
 32 {
 33 struct sembuf sbuf[2] =
 34 {
 35 {left_fno, -1, SEM_UNDO},
 36 {right_fno, -1, SEM_UNDO}
 37 };
 38
 39 if(semop(sem_id, sbuf, 2) == -1) {
 40 ERR_EXIT("P");
 41 }
 42 }
 43 /*对信号量数组编号为no的信号量做V操作
 44 哲学家放回叉子
 45 */
 46 void V(int sem_id,int left_fno,int right_fno){
 46 void V(int sem_id,int left_fno,int right_fno){
 47
 48 /*
 49 sbuf.sem_num:序号
 50 sbuf.sem_op:操作,1表示V操作
 51 sem_flg:设置信号量的操作
 52 */
 53 struct sembuf sbuf[2] =
 54 {
 55 {left_fno, 1, SEM_UNDO},
 56 {right_fno, 1, SEM_UNDO}
 57 };
 58
 59 if(semop(sem_id, sbuf, 2) == -1) {
 60 ERR_EXIT("V");
 61 }
 62 }
 63 void think(int tk_no){
 64 printf("Philosopher %d is thinking\n", tk_no);
 65 sleep(TIME_WAIT);
 66 }
 67
 68 void eat(int tk_no){
 69 printf("Philosopher %d is eating\n", tk_no);
 70 sleep(TIME_WAIT);
 71 }
 72
 73 void hungry(int tk_no){
 74 printf("Philosopher %d is hungry\n", tk_no);
 75 }
 76
 77 void philosopere(int sem_id,int tk_no)
 78 {
 79 srand(getpid());
 80 int left_fno=tk_no; //left_fno左边叉子编号
 81 int right_fno=(left_fno+1)%FORK_NUM;
 82 while(1)
 83 {
 84 think(tk_no);//思考
 85 hungry(tk_no);//饥饿
 86 //拿取一双叉子
 87 P(sem_id,left_fno,right_fno);
 88 eat(tk_no);//就餐
 89 //放下叉子
 90 V(sem_id,left_fno,right_fno);
 91 }
 92 }
 93 int main()
 94 {
 95 //1.创建 Semaphore(计数信号量)
 96 //创建信号集,其中有SEMS_NUM个信号量
 97 int sem_id = semget(IPC_PRIVATE, SEMS_NUM, IPC_CREAT | 0666);
 98 if (sem_id == -1){
 99 ERR_EXIT("semget");
100 }
101
102 //2.初始化 Semaphore
103 union semun sem;
104 sem.val = 1;
105 int i;
106 for (i = 0; i < SEMS_NUM; i++){
107 if(semctl(sem_id, i, SETVAL, sem)==-1){
108 ERR_EXIT("semctl");
109 }
110 }
111
112 int tk_no = 0;//哲学家编号
113 pid_t pid;
114 //创建子进程
115 for (i = 1; i < THINKER_NUM; i++)
116 {
117 pid = fork();
118 if (pid == -1){
119 ERR_EXIT("fork");
120 }
121 if (pid == 0){
122 tk_no = i;
123 break;
124 }
125 }
126
127 philosopere(sem_id,tk_no);//处理就餐
128
129 return 0;
130 }

4. 运行结果并解释

 结果解释:

  本题有五个进程,对应着五个哲学家。首先哲学家进行思考,接着就是饥饿,最后哲学家同时拿起两边的叉子进餐,

每一轮只有两位哲学家能够进餐,另外三位需要等待,并且这两位哲学家不能相邻,实验结果中首先是一号和四号哲学

家先进餐,这两个哲学家并不相邻。结果表示哲学家的进餐过程没有停,解决了上面的死锁问题。

 

参考文献:https://blog.csdn.net/qq_28602957/article/details/53538329

posted @ 2020-05-29 22:42  waiting!  阅读(265)  评论(0编辑  收藏  举报