假设理发店的理发室中有 3 个理发椅子和 3 个理发师,有一个可容纳 4 个顾客坐等理发的沙发。此外还有一间等候室,可容纳 13 位顾客等候进入理发室。顾客如果发现理发店中顾客已满(超过 20 人),就不进入理发店。 在理发店内,理发师一旦有空就为坐在沙发上等待时间最长的顾客理发,同时空出的沙发让在等候室中等待时间最长的的顾客就坐。顾客理完后,可向任何一位理发师付款。但理发店只有一本现金登记册,在任一时刻只能记录一个顾客的付款。理发师在没有顾客的时候就坐在理发椅子上睡眠。理发师的时间就用在理发、收款、睡眠上。 请利用 linux 系统提供的 IPC 进程通信机制实验并实现理发店问题的一个解法。
参考解法伪代码: 理发师程序(Barber) { 建立一个互斥帐本信号量: s_account,初值=1; 建立一个同步顾客信号量: s_customer,初值=0; 建立沙发消息队列: q_sofa; 建立等候室消息队列:q_wait; 建立 3 个理发师进程: b1_pid, b2_pid, b3_pid; 每个理发师进程作: while(1) { 以阻塞方式从沙发队列接收一条消息, 如果有消息,则消息出沙发队列(模拟一顾客理发); 唤醒顾客进程(让下一顾客坐入沙发)。 用进程休眠一个随机时间模拟理发过程。 理完发,使用帐本信号量记账。 互斥的获取账本 记账 唤醒用账本理发师者 否则没有消息(沙发上无顾客) 则理发师进程在沙发队列上睡眠; 当沙发队列有消息时被唤醒(有顾客坐入沙发)。 }
} 顾客程序(customer) { while(1) { 取沙发队列消息数(查沙发上顾客数) ; 如果消息数小于 4(沙发没座满) 以非阻塞方式从等候室队列接收一条消息(查等候室有顾客否), 如果有消息将接收到的消息发送到沙发队列(等候室顾客坐入沙发); 否则发送一条消息到沙发队列(新来的顾客直接坐入沙发); 否则(沙发坐满) 取等候室队列消息数(查等候室顾客数) ; 如果消息数小于 13 发送一条消息到等候室队列(等候室没满,新顾客进等候室); 否则 在顾客同步信号量上睡眠(等候室满暂不接待新顾客); 用进程休眠一个随机时间模拟顾客到达的时间间隔。 } } 开发调试过程: (不知为何贴上去注释有乱码,UTF-8) 新建一个文件夹,在该文件夹中建立以下名为 ipc.h 的 C 语 言头文件。 再建 立以下名为 ipc.c 的 C 语言头文件,实现头文件中申明的函数。 再建立以下名 为 barber.c 的 C 语 言 头 文 件 , 实 现 理 发 师 的 功 能 。 再 建 立 以 下 名 为 consumer.c 的 C 语言头文件,实现消费者的功能. 建立项 管理文件 Makefile 。使用 make 命令编译连接生成可执行的 barber 和 consumer 程序 ,执行并调试 producer 和 consumer 程序 /* * Filename * copyright * Function : ipc.h : (C) 2013 by zhengdujin : 声Θ明÷ IPC 机ú制的函ˉ数簓原-型í和í全局变量 */ #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 2 #define REQUEST 1 //理え发ぁ请求ó标括识/*信号灯控制用的共2同体*/ typedef union semuns { int val; } Sem_uns; /* 消息¢结á构1体*/ typedef struct msgbuf { long mtype; int mid; } Msg_buf; int wait_flg; key_t wait_key; int q_wait; int sofa_flg; key_t sofa_key; int q_sofa; int get_ipc_id(char *proc_file,key_t key); 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); //互¥斥a账本信号量 key_t a_mtx_key; int s_account; //同步顾客í信号量 key_t c_syn_key; int s_customer; int sem_val; int sem_flg; ******** /* * Filename * copyright * Function : ipc.c : (C) 2013 by zhengdujin : 一组哩建¨立ⅰIPC 机ú制的函ˉ数簓 */ #include "ipc.h" /* * get_ipc_id() 从洙proc/sysvipc/文件t系μ统中D获取 IPC 的 id 号* pfile: 对应畖/proc/sysvipc/目录中D的 IPC 文件t分别纄为a * msg-消息¢队ó列 ,sem-信号量,shm-共2享í内ú存 * 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:操ù作痢信号灯的结á构1 */ 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 信号灯数簓组哩中D信号灯的个数簓 * sem_flag 信号等台数簓组哩的存取权ā限T */ int set_sem(key_t sem_key,int sem_val,int sem_flg) { int sem_id; Sem_uns sem_arg; //测a试由 sem_key 标括识的信号灯数簓组哩是否已经-建¨立ⅰ if((sem_id = get_ipc_id("/proc/sysvipc/sem",sem_key)) < 0 ) { //semget 新建¨一个信号灯,其标括号返う回到 sem_id if((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 个字节ú 的共2享í内ú存区 * 如果建¨立ⅰ成é功|,返う回 一个指向ò该内ú存区首骸地址·的指针 shm_buf * 输入参数簓: * shm_key 共2享í内ú存的键ü值μ * shm_val 共2享í内ú存字节ú的长¤度è * shm_flag 共2享í内ú存的存取权ā限T */ char * set_shm(key_t shm_key,int shm_num,int shm_flg) { int i,shm_id; char * shm_buf; //测a试由 shm_key 标括识的共2享í内ú存区是否已经-建¨立ⅰ if((shm_id = get_ipc_id("/proc/sysvipc/shm",shm_key)) < 0 ) { //shmget 新建¨ 一个长¤度è为a shm_num 字节ú的共2享í内ú存,其标括号返う回到 shm_id if((shm_id = shmget(shm_key,shm_num,shm_flg)) <0) { perror("shareMemory set error"); exit(EXIT_FAILURE); } //shmat 将由 shm_id 标括识的共2享í内ú存附加ó给指针 shm_buf if((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; //初始为a 0 } //shm_key 标括识的共2享í内ú存区已经-建¨立ⅰ将由 shm_id 标括识的共2享í内ú存附加ó给指针 shm_buf if((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 消息¢队ó列 的存取权ā限T */ int set_msq(key_t msq_key,int msq_flg) { int msq_id; //测a试由 msq_key 标括识的消息¢队ó列 是否已经-建¨立ⅰ if((msq_id = get_ipc_id("/proc/sysvipc/msg",msq_key)) < 0 ) { //msgget 新建¨一个消息¢队ó列 ,其标括号返う回到 msq_id if((msq_id = msgget(msq_key,msq_flg)) < 0) { perror("messageQueue set error"); exit(EXIT_FAILURE); } } return msq_id; } ******** /* * Filename * copyright * Function : customer.c : (C) 2013 by zhengdujin : 一组哩建¨立ⅰIPC 机ú制的函ˉ数簓 */ #include "ipc.h" #include <signal.h> #include <unistd.h> #include <wait.h> int main(int argc,char *argv[]) { Sem_uns sem_arg; Msg_buf msg_arg; struct msqid_ds msg_wait_inf;struct msqid_ds msg_sofa_inf; //进程ì自定¨义的键ü盘ì中D断信号处鋦理え函ˉ数簓 typedef void (*sighandler_t) (int); void sigcat(){ printf("clear -s -q \n"); semctl(s_account,1,IPC_RMID,sem_arg); semctl(s_customer,1,IPC_RMID,sem_arg); msgctl(q_sofa, IPC_RMID,&msg_sofa_inf); msgctl(q_wait, IPC_RMID,&msg_wait_inf); } a_mtx_key=202; c_syn_key=201; sem_flg = IPC_CREAT | 0644; //互¥斥a账本信号量 sem_val =1; s_account=set_sem(a_mtx_key,sem_val,sem_flg); //同步顾客í信号量 sem_val =0; s_customer=set_sem(c_syn_key,sem_val,sem_flg); //建¨立ⅰ沙发ぁ消息¢队ó列 sofa_flg = IPC_CREAT| 0644; sofa_key = 301; q_sofa= set_msq(sofa_key,sofa_flg); //建¨立ⅰ等台候ò室酣消息¢队ó列 wait_flg = IPC_CREAT|0644; wait_key = 302; q_wait = set_msq(wait_key,wait_flg); msg_arg.mtype=REQUEST; int count; count=1; signal(SIGINT,(sighandler_t)sigcat); int rate; rate=2;//sleep 的时骸间 while(1) { //取沙发ぁ队ó列 的消息¢数簓,查é看′沙发ぁ顾客í数簓 int n_sofa; msgctl(q_sofa, IPC_STAT,&msg_sofa_inf); n_sofa=msg_sofa_inf.msg_qnum; //如果消息¢数簓小于 (辍沙发ぁ没座哩椅) if(n_sofa<4){ //沙发ぁ没座哩满ú//以非阻哩塞方式从洙等台待鋣室酣队ó列 接ó收一条消息¢ //如果有瓺消息¢将接ó收到的消息¢发ぁ送í到沙发ぁ队ó列 (辍等台候ò室酣顾客í做如沙 发ぁ); wait_flg = IPC_NOWAIT; if(msgrcv(q_wait,&msg_arg,sizeof(msg_arg),REQUEST,wait_flg) >= 0){ int num; num=msg_arg.mid; msgsnd(q_sofa,&msg_arg,sizeof(msg_arg),0); printf("%d 号顾客í从洙等台候ò室酣坐入沙发ぁ閈n",num); } else{ //否则ò发ぁ送í一条消息¢到沙发ぁ队ó列 (辍新来ぁ的顾客í直±接ó坐入沙发ぁ中D); sleep(rate); msg_arg.mid=count; msgsnd(q_sofa,&msg_arg,sizeof(msg_arg),0); printf("%d 号新顾客í坐入沙发ぁ閈n",count++); } }else{ //沙发ぁ坐满ú //取等台候ò室酣队ó列 消息¢数簓(查é等台候ò室酣顾客í数簓) int wait_n; msgctl(q_wait, IPC_STAT,&msg_wait_inf); wait_n=msg_wait_inf.msg_qnum; //如果消息¢数簓小于 3 if(wait_n<13) { //发ぁ送í一条消息¢到等台候ò室酣队ó列 (辍等台候ò室酣没做满ú,新顾客í进等台候ò室酣) int n; sleep(rate); msg_arg.mid=count; msgsnd(q_wait,&msg_arg,sizeof(msg_arg),0); printf("沙发ぁ坐满ú %d 号顾客í在ú等台候ò室酣等台候ò\n",count++); } else{ //在ú顾客í同步信号量上睡ˉ眠(辍等台候ò室酣满ú。£暂Y不接ó客í); printf("等台候ò室酣满ú %d 号顾客í没有瓺进入理え发ぁ店台篭n",count++); down(s_customer); //用进程ì休Y眠一个随机ú时骸间模£拟a顾客í到达的时骸间间隔。£ sleep(10);} } } } ******** /* * Filename * copyright * Function : barber.c : (C) 2013 by zhengdujin : 一组哩建¨立ⅰIPC 机ú制的函ˉ数簓 */ #include "ipc.h" #include <signal.h> #include <unistd.h> #include <wait.h> int main(int argc,char *argv[]) { Sem_uns sem_arg; struct msqid_ds msg_wait_inf; struct msqid_ds msg_sofa_inf; Msg_buf msg_arg; //进程ì自定¨义的键ü盘ì中D断信号处鋦理え函ˉ数簓 typedef void (*sighandler_t) (int); void sigcat(){ printf("clear -s -q \n"); semctl(s_account,1,IPC_RMID,sem_arg); semctl(s_customer,1,IPC_RMID,sem_arg); msgctl(q_sofa, IPC_RMID,&msg_sofa_inf); msgctl(q_wait, IPC_RMID,&msg_wait_inf); } a_mtx_key=202; c_syn_key=201; sem_flg = IPC_CREAT | 0644; //互¥斥a账本信号量 sem_val =1; s_account=set_sem(a_mtx_key,sem_val,sem_flg); //同步顾客í信号量 sem_val =0;s_customer=set_sem(c_syn_key,sem_val,sem_flg); //建¨立ⅰ沙发ぁ消息¢队ó列 sofa_flg = IPC_CREAT| 0644; sofa_key = 301; q_sofa= set_msq(sofa_key,sofa_flg); //建¨立ⅰ等台候ò室酣消息¢队ó列 wait_flg = IPC_CREAT|0644; wait_key = 302; q_wait = set_msq(wait_key,wait_flg); signal(SIGINT,(sighandler_t)sigcat); int i; int pid[3]; for(i=0;i<3;i++){ pid[i]=fork(); if(pid[i]==0) { while(1){ printf("%d号理え发ぁ师簗睡ˉ眠。£。£。£\n",getpid()); if(msgrcv(q_sofa,&msg_arg,sizeof(msg_arg),REQUEST,0) >=0) {//如果有瓺消息¢,则ò消息¢出沙发ぁ队ó列 (模£拟a一顾客í理え发ぁ; printf("%d 号 理 え 发 ぁ 师 簗 为 a%d 号 顾 客 í 理 え 发 ぁ 閈 n",getpid(),msg_arg.mid); up(s_customer); sleep(8);//模£拟a理え发ぁ时骸间 down(s_account);//理え完 发ぁ使用帐ê本信号量记账。£互¥斥a账本 printf("%d号理え发ぁ师簗收取%d号顾客í交费\n",getpid(),msg_arg.mid); sleep(2);//模£拟a交费时骸间 up(s_account);//允ê许í下一个顾客í进入 } } } } wait(NULL); } Makefile: hdrs = ipc.h r_src = barber.c ipc.c r_obj = barber.o ipc.o w_src = customer.c ipc.cw_obj = customer.o ipc.o opts = -g -c all: barber customer barber: $(r_obj) gcc $(r_obj) -o barber barber.o: $(r_src) $(hdrs) gcc $(opts) $(r_src) customer: $(w_obj) gcc $(w_obj) -o customer customer.o: $(w_src) $(hdrs) gcc $(opts) $(w_src) clean: rm barber customer *.o