题目详情如下:

1. 睡在北极商店中的圣诞老人只能被下述情形之一唤醒:
(1)所有9头驯鹿都从南太平洋度假归来。
(2)有些小精灵在制作玩具时遇到了麻烦:为了让圣诞老人们多休息一会儿,只能在3个小精灵遇到麻烦时才能叫醒圣诞老人。在这3个小精灵解决它们的问题时,其他想要找圣诞老人的小精灵只能等这3个小精灵返回。如果圣诞老人醒来后发现3个小精灵及最后一头从热带度假归来的驯鹿在店门口等着,那么圣诞老人就决定让这些小精灵等到圣诞节以后,因为准备雪橇更加重要(假设驯鹿不想离开热带,因此它们要在那里待到最后可能的时刻)。最后的驯鹿一定要赶回来找圣诞老人,在套上雪橇之前,驯鹿会在温暖的棚子里等着。用信号量解决上述问题,编写Linux程序或者Windows程序。
【提示】定义驯鹿计数整型变量rein_ct和精灵计数整型变量elve_ct。设置wake_sem同步信号量,用于驯鹿或精灵唤醒圣诞老人。设置slei_sem同步信号量,用于驯鹿在棚子里等待雪橇。设置mutex互斥信号量,用于互斥访问驯鹿计数变量或精灵计数变量,定义elve_mutex用来阻止3个精灵以外的精灵。如图4.2所示。


图4.2 圣诞老人问题信号量设置




这道题困扰了我很多天,每次写都很多问题,今天终于彻底解决了!下面来说说这道题到底怎么解答,让大家少走我的弯路。
首先:这是一道经典的同步与互斥问题,涉及到的同步信号量和互斥信号量的使用要弄明白,比如P操作和V操作。
其次:要去理清题目当中的同步、互斥逻辑顺序。
同步问题:(1)精灵遇到困难向圣诞老人申请帮助要先遇到困难,再唤醒圣诞老人。(2)9只驯鹿回归唤醒圣诞老人,要先9只回归,再唤醒圣诞老人。(3)圣诞老人要先做好雪橇,9只驯鹿才能去送玩具。
互斥问题:(1)每三只精灵互斥申请圣诞老人(也就是当已经有3只精灵申请圣诞老人时,其他精灵就不能申请);(2)互斥修改正在等待的精灵数量,互斥修改已回归的驯鹿数量。
再然后,(其实到这一步大家都没啥太大的问题,毕竟第二步算法网上也有)真正难到我的还是没有透彻理解linux进程的性质。一开始,我根本不知道进程之间全局变量并不是共享的,父进程和子进程之间只是共享代码块,而每个进程都是有各自的全局变量的(也就是说,每个子进程都有全局变量的副本,如果父进程或者一个子进程改变全局变量的值时,除了这个改变全局变量的进程里面全局变量的值会改变,其他进程还是保持自己的全局变量)。
知晓到这一步也不容易,花了几天,还没知道的这一步的时候真的是百思不得其解为什么santa进程的变量就是不变。之后便是疯狂寻找,全局变量怎么共享。找了很多,不知道为啥都对我没啥用(可能是我太菜了,害)。直到找到了这篇博客.才拯救了困惑了这么多天的我。主要是通过设置共享内存。




下面便是我这么多天写出来的全部解答了,已经完成了题目的要求。希望对大家有帮助啦。


#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<sys/wait.h>
#include<unistd.h>
#include<sys/shm.h>typedef enum __bool { false = 0, true = 1, } bool;struct shareMemory {int elve_ct;     //驯鹿计数整型变量int rein_ct;    //精灵计数整型变量
};int shm_id;
void *shm=NULL;//共享内存的首地址
struct shareMemory *shared;int wake_sem; // 同步信号量,用于驯鹿或精灵唤醒圣诞老人
int slei_sem; // 同步信号量,用于驯鹿在棚子里等待雪橇int rein_mutex; // 互斥信号量,用于互斥访问驯鹿计数变量或精灵计数变量
int elve_mutex; //用来阻止3个以外的精灵int rein_wait;
int elve_wait;int P(int sid) //P操作
{struct sembuf sb={0,-1,0}; //值减一return semop(sid, &sb, 1); //执行操作
} //返回0成功,返回-1失败int V(int sid) //V操作
{struct sembuf sb={0,1,0};  //值加1return semop(sid, &sb, 1); //执行操作
}; //返回0成功,返回-1失败void make_toy()  //小精灵制作玩具
{sleep(1);//printf("小精灵在制作玩具\n");
}void prepare_sled()  //圣诞老人准备雪橇
{printf("圣诞老人准备雪橇\n\n");sleep(1);
}void reindeerApp()  //驯鹿
{shm=shmat(shm_id,0,0);  //将共享内存连接到当前进程的地址空if(shm==(void *)-1){perror("Fail to shmat");exit(EXIT_SUCCESS);}shared = (struct shareMemory *)shm; //设置共享内存shared->rein_ct=0;//printf(" rein Memory attached at %x\n",(int)shm);//printf("rein的初始数量为: %d\n",shared->rein_ct);  while(true) {P(rein_wait); //最多只有9只驯鹿回归sleep(1);//printf("正在度假的驯鹿要回归了\n");P(rein_mutex); //进入临界区shared->rein_ct++; //驯鹿数加1if(shared->rein_ct==9) //最后一头驯鹿回归,叫醒圣诞老人{  printf("最后一只驯鹿回归,叫醒圣诞老人\n");V(rein_mutex);V(wake_sem); //发送信号唤醒圣诞老人P(slei_sem); //雪橇做好了吗?printf("雪橇已准备好,驯鹿去送玩具了\n");}else  { //睡在棚子里面等待雪橇V(rein_mutex);printf("第%d只驯鹿回归了\n",shared->rein_ct);    }}if(shmdt(shm)==-1)   //把共享内存从当前进程分离{perror("Fail to shmat");exit(EXIT_SUCCESS);}if(shmctl(shm_id,IPC_RMID,0)==-1)   //删除这块共享内存{ perror("Fail to shmctl(IPC_RMID)");exit(EXIT_SUCCESS);}exit(EXIT_SUCCESS);}void santaApp()    //圣诞老人
{shm=shmat(shm_id,0,0);  //将共享内存连接到当前进程的地址空if(shm==(void *)-1){perror("Fail to shmat");exit(EXIT_SUCCESS);}shared = (struct shareMemory *)shm; //设置共享内存shared->elve_ct=0;shared->rein_ct=0;//printf(" santa Memory attached at %x\n",(int)shm);while(true) {//printf("我是santa--APP\n");P(wake_sem); //等待被唤醒//printf("\n圣诞老人被唤醒\n");//printf("\n");//printf("驯鹿%d只\n",shared->rein_ct);if(shared->rein_ct==9)  //如果满足最后一头驯鹿回归,则首先处理驯鹿.每次都先判断{printf("九只驯鹿到齐\n");P(rein_mutex);shared->rein_ct=0; //驯鹿数置为0V(rein_mutex);prepare_sled(); //制作雪橇 V(slei_sem);}//printf("精灵%d只\n",shared->elve_ct);if(shared->elve_ct==3) {// printf("准备为精灵解决麻烦\n");printf("圣诞老人在解决3个小精灵的问题\n");printf("\n");sleep(1);P(elve_mutex);shared->elve_ct=0; //等待精灵数置为0    V(elve_mutex);int i; for(i=0;i<3;i++) V(elve_wait);}} //whileif(shmdt(shm)==-1)   //把共享内存从当前进程分离{perror("Fail to shmat");exit(EXIT_SUCCESS);}if(shmctl(shm_id,IPC_RMID,0)==-1)   //删除这块共享内存{ perror("Fail to shmctl(IPC_RMID)");exit(EXIT_SUCCESS);}exit(EXIT_SUCCESS);}//void void elvesApp()    //小精灵
{shm=shmat(shm_id,0,0);   //将共享内存连接到当前进程的地址空if(shm==(void *)-1){perror("Fail to shmat");exit(EXIT_SUCCESS);}shared = (struct shareMemory *)shm; //设置共享内存shared->elve_ct=0;//printf(" elve Memory attached at %x\n",(int)shm);//printf("elve的初始数量为: %d\n",shared->elve_ct);  while(true) {//printf("我是elve--APP\n");make_toy(); //制作玩具 P(elve_wait); //一次性唤醒圣诞老人最多三只精灵申请//printf("遇到麻烦,申请帮助\n"); P(elve_mutex); //互斥需要帮助shared->elve_ct++;if(shared->elve_ct==3)  { printf("3只精灵要发信号唤醒老人了\n");V(elve_mutex);V(wake_sem); //发信号唤醒圣诞老人}    else {V(elve_mutex); printf("%d只精灵等待\n",shared->elve_ct);}    }if(shmdt(shm)==-1)   //把共享内存从当前进程分离{perror("Fail to shmat");exit(EXIT_SUCCESS);}if(shmctl(shm_id,IPC_RMID,0)==-1)   //删除这块共享内存{ perror("Fail to shmctl(IPC_RMID)");exit(EXIT_SUCCESS);}exit(EXIT_SUCCESS);}int main(int argc,char *argv[])
{pid_t p1,p2,p3;#define WAKE_KEY 1#define SLEI_KEY 2#define REINMUTEX_KEY 3#define ELVEMUTEX_KEY 4#define REINWAIT_KEY 5#define ELVEWAIT_KEY 6#define shm_KEY1 1001
#define shm_KEY2 1002if((shm_id = shmget(shm_KEY1,1024,0666|IPC_CREAT))==-1)  //创建共享内存{perror("Fail to shmget");exit(EXIT_SUCCESS);}//printf("share memory id: %d\n",shm_id);wake_sem=semget(WAKE_KEY,1,IPC_CREAT|0660);  //创建信号量wake_semsemctl(wake_sem,0,SETVAL,0);         //信号量wake_sem值初始化0slei_sem=semget(SLEI_KEY,1,IPC_CREAT|0660);  //创建信号量slei_semsemctl(slei_sem,0,SETVAL,0);         //信号量slei_sem值初始化0rein_mutex=semget(REINMUTEX_KEY,1,IPC_CREAT|0660);  //创建信号量mutexsemctl(rein_mutex,0,SETVAL,1);         //信号量rein_mutex值初始化1elve_mutex=semget(ELVEMUTEX_KEY,1,IPC_CREAT|0660);  //创建信号量elve_mutexsemctl(elve_mutex,0,SETVAL,1);         //信号量elve_mutex值初始化1rein_wait=semget(REINWAIT_KEY,1,IPC_CREAT|0660);  //创建信号量elve_mutexsemctl(rein_wait,0,SETVAL,9);         //信号量rein_wait值初始化9elve_wait=semget(ELVEWAIT_KEY,1,IPC_CREAT|0660);  //创建信号量elve_mutexsemctl(elve_wait,0,SETVAL,3);         //信号量elve_wait值初始化3while((p1=fork())==-1);           //创建子进程if(!p1) reindeerApp();                  //驯鹿的行为else {while((p2=fork())==-1);         //创建子进程if(!p2)   santaApp();                //圣诞老人的行为else  {while((p3=fork())==-1);      //创建子进程if(!p3)   elvesApp();           //小精灵的行为}}wait(0);                               //等待子进程结束wait(0);                               //等待子进程结束wait(0);                               //等待子进程结束semctl(wake_sem, 0, IPC_RMID);            //删除信号量wake_semsemctl(slei_sem, 0, IPC_RMID);            //删除信号量slei_semsemctl(rein_mutex, 0, IPC_RMID);               //删除信号量mutexsemctl(elve_mutex, 0, IPC_RMID);          //删除信号量elve_mutexsemctl(rein_wait, 0, IPC_RMID);               //删除信号量rein_waitsemctl(elve_wait, 0, IPC_RMID);          //删除信号量elve_waitreturn 0;
}




代码运行结果如下:


经典圣诞老人题----同步与互斥相关推荐

  1. 写出记录型信号量中的wait操作代码_操作系统进程的同步与互斥及经典同步与互斥问题...

    概览 临界区临界区的引入 在系统当中,有些资源允许多个进程共享(磁盘),有些资源只允许进程单独使用(打印机,共享变量).为了让进程单独使用资源而不受其他进程干扰引入了临界区的概念. 临界区的概念 在一 ...

  2. 计算机操作系统——经典进程的同步问题

    计算机操作系统--信号量机制与经典进程的同步问题 信号量机制 随着发展,信号量从整型信号量经记录型信号量,进而发展为"信号量集"机制. 一般来说,信号量的值与相应的资源的使用情况有 ...

  3. 经典算法题每日演练——第十题 树状数组

    原文:经典算法题每日演练--第十题 树状数组 有一种数据结构是神奇的,神秘的,它展现了位运算与数组结合的神奇魅力,太牛逼的,它就是树状数组,这种数据结构不是神人是发现不了的. 一:概序 假如我现在有个 ...

  4. java 初级编程题_java基础经典编程题

    java基础经典编程题 Monkey_peach代码 package com.sailor.game; /** * 题目:猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个 第 ...

  5. [一个经典的多线程同步问题]解决方案一:关键段CS

    前面提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题. 本文先介绍如何使用关键段,然后再深层次的分析下关键段的实现机制和原理. 关键段CRITICA ...

  6. 用信号量解决进程的同步与互斥

    转自:http://www.cnblogs.com/whatbeg/p/4435286.html 现代操作系统采用多道程序设计机制,多个进程可以并发执行,CPU在进程之间来回切换,共享某些资源,提高了 ...

  7. java什么时候需要同步_JAVA中线程在什么时候需要同步和互斥

    JAVA中线程在什么时候需要同步和互斥 关注:265  答案:6  mip版 解决时间 2021-01-27 08:10 提问者时光易老 2021-01-27 03:32 JAVA中线程在什么时候需要 ...

  8. 【操作系统】同步和互斥

    进程之间可能存在同步和互斥的制约关系. 同步指的是为了完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调它们的工作次序而等待.传递消息所产生的的制约关系. 互斥指的是一个进程进入临界 ...

  9. 进程的同步和互斥反映了_用信号量解决进程的同步与互斥探讨【持续更新】

    现代操作系统采用多道程序设计机制,多个进程可以并发执行,CPU在进程之间来回切换,共享某些资源,提高了资源的利用率,但这也使得处理并发执行的多个进程之间的冲突和相互制约关系成为了一道难题.如果对并发进 ...

  10. 数字IC设计工程师笔试面试经典100题-有答案

    转自知乎答主ictown_数字IC设计工程师笔试面试经典100题-有答案-陈恩 1:什么是同步逻辑和异步逻辑?(汉王) 同步逻辑是时钟之间有固定的因果关系.异步逻辑是各时钟之间没有固定的因果关系. 同 ...

最新文章

  1. python 打印皮卡丘_Python到底是什么?学姐靠它拿了5个offer
  2. linux 格式化未分配区间,Linux 格式化分区 报错Could not stat --- No such file or directory 和 partprobe 命令...
  3. 文武双全,AI 女神们的修炼手册!
  4. MVC与WebApi中的异常过滤器
  5. nginx的error.log日志常见的几个错误解决方法
  6. 多面性的打赏功能,由直播行业引发的一点思考
  7. 怎么导入sklearn包_在导入sklearn包是报错
  8. e语言html显示框,html marguee标签
  9. linux卸载cuda10.0,Ubuntu下安装CUDA10.0以及问题
  10. 谈计算机知识对学生的作用,浅谈计算机在教学中的作用
  11. 外呼机器人起名_电销外呼机器人如此受欢迎,今天终于知道原因了
  12. Javascript 判断浏览器是否为IE的最短方法
  13. 工单发料,退料等一些物料的移动
  14. 【贪心+堆优化】奶牛晒衣服 dry.pas/c/cpp
  15. 虚拟机SSH免密登录配置
  16. git版本管理软件——git储藏
  17. 嵌入式软件开发笔试面试知识点总结-Linux部分
  18. js实现上下左右移动小方块
  19. python安装包下载及安装教程
  20. 集线器,转发器,网桥,以太网交换机

热门文章

  1. 病毒入侵:全靠分布式 Gossip 协议
  2. poi根据模版导出多页word,带插入图片,并压缩下载
  3. iOS Mac安装ipa文件的几种方法
  4. 基于matlab的排队系统仿真
  5. Byethost美国免费空间免费撸
  6. 循环el-color-picker修改echarts饼图配色
  7. 数据结构之队列(链式队列)的基本操作与实现
  8. spring @lazy注解的使用
  9. 智己让上汽的高端梦第一次落地
  10. 植物大战僵尸 辅助 总结