当前位置:文档之家› 操作系统实验五 理发师问题

操作系统实验五 理发师问题

实验题目:理发师问问题学号:201000130133 班级: 2010级计算机4班姓名:郑思雨 Email:1412561943@实验目的:1、进一步研究和实践操作系统中关于并发进程同步与互斥操作的一些经典问题的解法。

2、加深对于非对称性互斥问题有关概念的理解。

观察和体验非对称性互斥问题的并发控制方法。

3、进一步了解Linux系统中IPC进程同步工具的用法,训练解决对该类问题的实际编程、调试和分析问题的能力。

硬件环境:微机(多核,4GB 以上内存,320GB 以上硬盘)软件环境:Ubuntu-Linux 操作系统Gnome 桌面gcc version 4.1.2gedit 2.18.2OpenOffice 2.3实验步骤:1、了解实验的目的,了解并掌握与进程间通信IPC中的3个对象:共享内存、信号灯数组、消息队列到呢个有关的系统调用,并能熟练的掌握。

2、阅读实验题目并分析实验的需求。

理发店问题:假设理发店的理发室中有3个理发椅子和3个理发师,有一个可容纳4个顾客坐等理发的沙发。

此外还有一间等候室,可容纳13位顾客等候进入理发室。

顾客如果发现理发店中顾客已满(超过20人),就不进入理发店。

在理发店内,理发师一旦有空就为坐在沙发上等待时间最长的顾客理发,同时空出的沙发让在等候室中等待时间最长的的顾客就坐。

顾客理完发后,可向任何一位理发师付款。

但理发店只有一本现金登记册,在任一时刻只能记录一个顾客的付款。

理发师在没有顾客的时候就坐在理发椅子上睡眠。

理发师的时间就用在理发、收款、睡眠上。

(1)首先创建ipc.h文件,在里面定义生产者/消费者共用的IPC函数的原型和变量。

(2)然后创建ipc.c文件,在里面定义生产者/消费者共用的IPC 的具体的相应函数。

(3)创建sofa_control文件,在里面声明两个消息队列,当沙发空闲时则会将空闲的消息放入相应的队列中,让顾客可以进入沙发中,当理发师空闲时,也会将相应的消息放入队列中,从而可以让顾客到理发椅上进行理发。

(4)创建room_control文件,在里面创建消息队列,当等待室里的人数少于十三个时,若从消息队列中获得信息,则顾客可以进入,当沙发上有空位时,在等待室的人可以进入沙发等待。

(5)创建customer文件,里面声明两个消息队列,当获得允许进入的信息时,顾客进入等待室,当顾客理完发则会离开,同时向消息队列中发送信息。

(6)创建barber文件,在里面声明三个队列,当理发师就绪则会向队列中发送消息,若顾客收到消息就会去理发,当顾客离开时,也会想队列中发送一条消息。

同时理发师理发与收账是互斥的。

通过上述的文件就可以实现相应的功能。

3、分析清楚实验要求,便开始用编程语言进行编程,将分析的思路用编程语言实现。

4、完成编写程序,要进行运行调试,找到编写中的错误,进行进一步的修改,直到程序运行过程中不再出现错误.5、完成程序的调试与修改,保存程序,对编程的过程进行总结,找到编程中的不足,并完成实验报告结论分析与体会:结论:利用 IPC机制中的消息队列来实现相应的功能。

通过往队列中放入信和从队列中获取信息来实现进程间的通信,当顾客少于三个时,理师可以接给顾客理发并收取费用;当顾客多于三个少于七个时顾客要在沙发等待,理发师给顾客理完发然后等待簿进行记账;当顾客多于七个时,四个顾客在沙发上等,其他的在等待室等待,直到沙发空出才能从等待室到沙发,然后每给一个顾客理完发,则理发师等待账簿进行记账。

通过建立多个消息队列就可以实现相应的功能。

体会:1、通过实验对进程同步与互斥概念有了更深入的了解,加深了解了进程同步与互斥的效果。

2、加深了对非对称性互斥问题有关概念的理解,并进一步掌握了对经典问题的解法。

3、进一步熟练掌握了进程间通信有关的系统调用,并学会用它们进行编程和调试。

4、通过实验对相应的知识有了深入的理解,对编程软件也更熟练的掌握。

代码实现如下:ipc.c代码部分:#include "ipc.h"/** get_ipc_id() 从/proc/sysvipc/文件系统中获取 IPC 的 id 号* pfile: 对应/proc/sysvipc/目录中的 IPC 文件分别为*msg-消息队列,sem-信号量,shm-共享内存* key: 对应要获取的 IPC 的 id 号的键值*/int get_ipc_id(char *proc_file,key_t key){FILE *pf;int i,j;char line[BUFSZ],colum[BUFSZ];if((pf = fopen(proc_file,"r")) == NULL){perror("Proc file not open");exit(EXIT_FAILURE);}fgets(line, BUFSZ,pf);while(!feof(pf)){i = j = 0;fgets(line, BUFSZ,pf);while(line[i] == ' ') i++;while(line[i] !=' ') colum[j++] = line[i++];colum[j] = '\0';if(atoi(colum) != key) continue;j=0;while(line[i] == ' ') i++;while(line[i] !=' ') colum[j++] = line[i++];colum[j] = '\0';i = atoi(colum);fclose(pf);return i;}fclose(pf);return -1;}/** 信号灯上的 down/up 操作* semid:信号灯数组标识符* semnum:信号灯数组下标* buf:操作信号灯的结构*/int down(int sem_id){struct sembuf buf;buf.sem_op = -1;buf.sem_num = 0;buf.sem_flg = SEM_UNDO;if((semop(sem_id,&buf,1)) <0) { perror("down error ");exit(EXIT_FAILURE);}return EXIT_SUCCESS;}int up(int sem_id){struct sembuf buf;buf.sem_op = 1;buf.sem_num = 0;buf.sem_flg = SEM_UNDO;if((semop(sem_id,&buf,1)) <0) {perror("up error ");exit(EXIT_FAILURE);}return EXIT_SUCCESS;}/** set_sem 函数建立一个具有 n 个信号灯的信号量*如果建立成功,返回一个信号灯数组的标识符 sem_id*输入参数:* sem_key 信号灯数组的键值* sem_val 信号灯数组中信号灯的个数* sem_flag 信号等数组的存取权限*/int set_sem(key_t sem_key,int sem_val,int sem_flg){int sem_id;Sem_uns sem_arg;//测试由 sem_key 标识的信号灯数组是否已经建立if((sem_id = get_ipc_id("/proc/sysvipc/sem",sem_key)) < 0 ){//semget 新建一个信号灯,其标号返回到 sem_idif((sem_id = semget(sem_key,1,sem_flg)) < 0){perror("semaphore create error");exit(EXIT_FAILURE);}//设置信号灯的初值sem_arg.val = sem_val;if(semctl(sem_id,0,SETVAL,sem_arg) <0){perror("semaphore set error");exit(EXIT_FAILURE);}}return sem_id;}/** set_shm 函数建立一个具有 n 个字节的共享内存区*如果建立成功,返回一个指向该内存区首地址的指针 shm_buf *输入参数:* shm_key 共享内存的键值* shm_val 共享内存字节的长度* shm_flag 共享内存的存取权限*/char * set_shm(key_t shm_key,int shm_num,int shm_flg){int i,shm_id;char * shm_buf;//测试由 shm_key 标识的共享内存区是否已经建立if((shm_id = get_ipc_id("/proc/sysvipc/shm",shm_key)) < 0 ){//shmget 新建一个长度为 shm_num 字节的共享内存,其标号返回到 shm_id if((shm_id = shmget(shm_key,shm_num,shm_flg)) <0){perror("shareMemory set error");exit(EXIT_FAILURE);}//shmat 将由 shm_id 标识的共享内存附加给指针 shm_bufif((shm_buf = (char *)shmat(shm_id,0,0)) < (char *)0){perror("get shareMemory error");exit(EXIT_FAILURE);}for(i=0; i<shm_num; i++) shm_buf[i] = 0; //初始为 0}//shm_key 标识的共享内存区已经建立,将由 shm_id 标识的共享内存附加给指针 shm_bufif((shm_buf = (char *)shmat(shm_id,0,0)) < (char *)0){perror("get shareMemory error");exit(EXIT_FAILURE);}return shm_buf;}/** set_msq 函数建立一个消息队列* 如果建立成功,返回一个消息队列的标识符 msq_id* 输入参数:* msq_key 消息队列的键值* msq_flag 消息队列的存取权限*/int set_msq(key_t msq_key,int msq_flg){int msq_id;//测试由 msq_key 标识的消息队列是否已经建立if((msq_id = get_ipc_id("/proc/sysvipc/msg",msq_key)) < 0 ) {//msgget 新建一个消息队列,其标号返回到 msq_idif((msq_id = msgget(msq_key,msq_flg)) < 0){perror("messageQueue set error");exit(EXIT_FAILURE);}}return msq_id;}ipc.h部分代码:#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/shm.h>#include <sys/sem.h>#include <sys/msg.h>#define BUFSZ 256#define MAXVAL 100#define STRSIZ 8#define CUSTOM 1 //读写完成标识/*信号灯控制用的共同体*/ typedef union semuns {int val;} Sem_uns;/* 消息结构体*/typedef struct msgbuf {long mtype;int mid;} Msg_buf;/*消息信息结构体typedef struct msqid_ds { struct ipc_perm msg_perm;struct msg *msg_first;struct msg *msg_last;time_t msg_stime;time_t msg_rtime;time_t msg_ctime;struct wait_queue *wwait;struct wait_queue *rwait;ushort msg_cbytes;ushort msg_qnum;ushort msg_qbytes;ushort msg_lspid;ushort msg_lrpid; }Msqid_ds;*/key_t book_key;// 记账簿信号灯int book_sem;int sem_flg;int quest_flg;key_t quest_key;//椅子消息队列int quest_id;int respond_flg;//等候室消息队列key_t respond_key;int respond_id;int get_ipc_id(char *proc_file,key_t key);//ipc.c中函数声明char *set_shm(key_t shm_key,int shm_num,int shm_flag);int set_msq(key_t msq_key,int msq_flag);int set_sem(key_t sem_key,int sem_val,int sem_flag);int down(int sem_id);int up(int sem_id);barber部分代码:#include "ipc.h"//int rand() { //用于产生生产者需要的随机数,随机数为0、1、2、3、4srand((unsigned)time(NULL));//return random() % 5;//}int main(int argc,char *argv[]){int i,j,k,t,flg =0,count,m = 0;int pid [3];Msg_buf msg_arg;struct msqid_ds msg_inf;//消息信息结构体//信号灯的值book_key = 101;sem_flg = IPC_CREAT | 0644;book_sem = set_sem(book_key,1,sem_flg);//建立一条沙发队列quest_flg = IPC_CREAT| 0644;quest_key = 201;quest_id = set_msq(quest_key,quest_flg);//建立一条等候室队列respond_flg = IPC_CREAT|0644;respond_key = 202;respond_id = set_msq(respond_key,respond_flg); //理发师程序执行循环过程printf("Wait customer \n");for(j = 0;j<3;j++)//循环的创建3个字程序{if((pid[j] = fork())<0){perror("process not create");exit(EXIT_FAILURE);}if(pid[j] == 0)break;}customer部分代码:#include "ipc.h"//int rand() { //用于产生生产者需要的随机数,随机数为0、1、2、3、4中的其一//srand((unsigned)time(NUL L));//return random() % 5;//}int main(int argc,char *argv[]){int i,j,k,t,count = 1;Msg_buf msg_arg;struct msqid_ds msg_inf;//消息队列信息结构体//创建信号灯book_key = 101;sem_flg = IPC_CREAT | 0644;book_sem = set_sem(book_key,1,sem_flg);//建立沙发消息队列quest_flg = IPC_CREAT| 0644;quest_key = 201;quest_id = set_msq(quest_key,quest_flg);//建立等候消息队列respond_flg = IPC_CREAT|0644;respond_key = 202;respond_id = set_msq(respond_key,respond_flg);//程序的主体,while循环msg_arg.mtype = CUSTOM;while(1){//t=rand();//sleep(t+5);//随机的休眠,模拟顾客到来的时间sleep(4);if(msgctl(quest_id, IPC_STAT,&msg_inf)== 0) //以阻塞方式接收消息{j = msg_inf.msg_qnum;//沙发队列的消息数if(j<4)//向沙发队列中发送一条消息{msg_arg.mid = count;//送入顾客的编号msgsnd(quest_id,&msg_arg,sizeof(msg_arg),0);printf("%d 号新顾客坐入沙发\n",count++);}if(j==4){if (msgctl(respond_id, IPC_STAT,&msg_inf)== -1)//取等候队列的消息结构体{printf("2 error\n");}k = msg_inf.msg_qnum;//等候队列的消息数if(k<13){msg_arg.mid = count;msgsnd(respond_id,&msg_arg,sizeof(msg_arg),0);//沙发队列已满等候队列发送消息printf("沙发坐满%d 号新顾客坐入等候室\n",count++);}else{printf("理发店人满\n");//理发店已满}}}else{printf("1 error\n");}}pause();return EXIT_SUCCESS;//程序结束}removeipc.c部分代码:#include "ipc.h"int main(){book_key = 101;quest_key = 201;respond_key = 202;int id;Sem_uns sem_arg;if((id=get_ipc_id("/proc/sysvipc/sem",book_key))>=0){//删除已经建立的信号灯semctl(id,0,IPC_RMID,sem_arg);}if((id=get_ipc_id("/proc/sy svipc/msg",quest_key))>=0){//删除已经建立的请求消息队列msgctl(id,IPC_RMID,NULL);if((id=get_ipc_id("/proc/sysvipc/msg",respond_key))>=0){//删除已经建立的响应消息队列msgctl(id,IPC_RMID,NULL);}return 0;}。

相关主题