进程同步实验张咪软件四班一、实验目的总结和分析示例实验和独立实验中观察到的调试和运行信息,说明您对与解决非对称性互斥操作的算法有哪些新的理解和认识?为什么会出现进程饥饿现象?本实验的饥饿现象是怎样表现的?怎样解决并发进程间发生的饥饿现象?您对于并发进程间使用消息传递解决进程通信问题有哪些新的理解和认识?根据实验程序、调试过程和结果分析写出实验报告。二、实验要求理发店问题:假设理发店的理发室中有3个理发椅子和3个理发师,有一个可容纳4个顾客坐等理发的沙发。此外还有一间等候室,可容纳13位顾客等候进入理发室。顾客如果发现理发店中顾客已满(超过20人),就不进入理发店。在理发店内,理发师一旦有空就为坐在沙发上等待时间最长的顾客理发,同时空出的沙发让在等候室中等待时间最长的的顾客就坐。顾客理完发后,可向任何一位理发师付款。但理发店只有一本现金登记册,在任一时刻只能记录一个顾客的付款。理发师在没有顾客的时候就坐在理发椅子上睡眠。理发师的时间就用在理发、收款、睡眠上。请利用linux系统提供的IPC进程通信机制实验并实现理发店问题的一个解法。三、实验环境实验环境均为Linux操作系统,开发工具为gcc和g++。四、实验思路约束:1.设置一个count变量来对顾客进行计数,该变量将被多个顾客进程互斥地访问并修改,通过一个互斥信号量mutext来实现。count20时,就不进入理发店。7count20时,count++,顾客申请等候室,进入等候室等待,用一个room信号量控制。然后等待申请沙发,用一个sofa信号量控制。然后申请椅子。3count7时,count++,顾客坐在沙发上等待,等待申请椅子。count3时,count++,顾客坐在椅子上等待理发。2.只有在理发椅空闲时,顾客才能做到理发椅上等待理发师理发,否则顾客便必须等待;只有当理发椅上有顾客时,理发师才可以开始理发,否则他也必须等待。可通过信号量empty和full来控制。3.理发师为顾客理发时,顾客必须等待理发的完成,并在理发完成后理发师唤醒他,使用一个信号量finish来控制;4.顾客理完发后必须向理发师付费,并等理发师收费后顾客才能离开;而理发师则需等待顾客付费,并在收费后唤醒顾客以允许他离开,这可分别通过两个信号量payment和receipt来控制。初值:计数intcount=0信号量empty=3;full=0;room=13;sofa=4;finish=0;pay=0;receipt=0;mutex=1;理发师进程while(1){wait(full);//等待理发椅上有顾客剪头发signal(finish);//通知顾客理发完成wait(pay);//等待顾客付费wait(mutex);//在任一时刻只能记录一个顾客的付款收费signal(mutex);signal(receipt);//通知顾客收费完毕}顾客进程wait(mutex);//count既用于判断,也要修改,所以为临界资源,用mutex管理互斥if(count20){//顾客大于20人signal(mutex);离开理发店}else{//顾客小于20人count=count+1;//进入理发店if(count7){//count7,说明理发椅和沙发上都有人,需要到等待室等待signal(mutex);wait(room);//申请进入等待室在等待室等wait(sofa);//申请沙发signal(room);//释放等待室坐在沙发上等wait(empty);//等待理发椅为空申请到理发椅signal(sofa);//释放沙发}elseif(count3){//说明理发椅上都有人,需要坐到沙发上等待signal(mutex);wait(sofa);//申请沙发坐在沙发上等wait(empty);//等待理发椅为空申请到理发椅signal(sofa);//释放沙发}else{//count3,可以坐到理发椅上等待signal(mutex);wait(empty);//申请理发椅}坐在理发椅上等待理发signal(full);//通知理发师开始理发理发wait(finish);//等待理发完毕付款signal(payment);//通知理发师已付款wait(receipt);//等待理发师收款理发师收费完成,顾客离开理发椅signal(empty);//释放理发椅wait(mutex);//对count临界资源操作,用mutex完成互斥count=count-1;//离开理发店signal(mutex);}七、调试及实验结果1、创建makefile文件hdrs=ipc.hopts=-g-cc_src=cons.cipc.cc_obj=cons.oipc.op_src=bar.cipc.cp_obj=bar.oipc.oall:producerconsumerconsumer:$(c_obj)gcc$(c_obj)-oconsumercons.o:$(c_src)$(hdrs)gcc$(opts)$(c_src)producer:$(p_obj)gcc$(p_obj)-oproducerbar.o:$(p_src)$(hdrs)gcc$(opts)$(p_src)clean:rmconsbar*.o2.执行make命令,结果出现了许多由于粗心造成的编译错误3、修改程序后编译成功,打开两个终端,先运行producer.c,再运行consumer.c4、若按ctrl+c停止producer进程,则出现如下图结果。沙发坐满后顾客将进入等候室等待5、若再次执行producer进程,将陆续唤醒在等待的顾客,结果如下图6、若再停止producer,让等待室的人也满,则顾客会离开理发店,结果如下图七、心得与收获1、本次试验,使我基本掌握了怎样用消息队列控制和堵塞进程,实现对共享内存的有序访问。2、msgrcv/msgsnd为linux系统中异步或进程间通信的一种机制,msgrcv()可以从消息队列中读取消息,msgsnd()将一个新的消息写入队列。intmsgsnd(intmsqid,constvoid*msgp,size_tmsgsz,intmsgflg);ssize_tmsgrcv(intmsqid,void*msgp,size_tmsgsz,longmsgtyp,intmsgflg);msgflg:这个参数依然是是控制函数行为的标志,取值可以是:0,表示忽略;IPC_NOWAIT,如果消息队列为空,则返回一个ENOMSG,并将控制权交回调用函数的进程。3、不仅加深了对进程互斥的理解,还使我加深了对理发师算法的理解,找到了它与读者写者问题的共同之处:(1).进程间的互斥(2).理发师类似读者进程,顾客类似写者进程。4、编写程序时要细心,对于编译过程中出现的错误,要有耐心去解决。八、源代码Ipc.h:#includestdio.h#includestdlib.h#includesys/types.h#includesys/ipc.h#includesys/shm.h#includesys/sem.h#includesys/msg.h#defineBUFSZ256#defineMAXVAL100#defineSTRSIZ8#defineWRITERQUEST1#defineREADERQUEST2#defineFINISHED3//写请求标识//读请求标识//读写完成标识typedefunionsemuns{intval;}Sem_uns;typedefstructmsgbuf{longmtype;intmid;}Msg_buf;//信号量key_tcostomer_key;intcostomer_sem;key_taccount_key;intaccount_sem;intsem_val;intsem_flg;//消息队列intwait_quest_flg;key_twait_quest_key;intwait_quest_id;intwait_respond_flg;key_twait_respond_key;intwait_respond_id;intsofa_quest_flg;key_tsofa_quest_key;intsofa_quest_id;intsofa_respond_flg;key_tsofa_respond_key;intsofa_respond_id;intget_ipc_id(char*proc_file,key_tkey);char*set_shm(key_tshm_key,intshm_num,intshm_flag);intset_msq(key_tmsq_key,intmsq_flag);intset_sem(key_tsem_key,intsem_val,intsem_flag);intdown(intsem_id);intup(intsem_id);Ipc.c:#includeipc.hintget_ipc_id(char*proc_file,key_tkey){FILE*pf;inti,j;charline[BUFSZ],colum[BUFSZ];if((pf=fopen(proc_file,r))==NULL){perror(Procfilenotopen);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);returni;}fclose(pf);return-1;}intdown(intsem_id){structsembufbuf;buf.sem_op=-1;buf.sem_num=0;buf.sem_flg=SEM_UNDO;if((semop(sem_id,&buf,1))0){perror(downerror);exit(EXIT_FAILURE);}returnEXIT_SUCCESS;}intup(intsem_id){structsembufbuf;buf.sem_op=1;buf.sem_num=0;buf.sem_flg=SEM_UNDO;if((semop(sem_id,&buf,1))0){perror(uperror);exit(EXIT_FAILURE);}returnEXIT_SUCCESS;}intset_sem(key_tsem_key,intsem_val,intsem_flg){intsem_id;Sem_unssem_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(semaphorecreateerror);exit(EXIT_FAILURE);}//设置信号灯的初值sem_arg.val=sem_val;if(semctl(sem_id,0,SETVAL,sem_arg)0){perror(semaphoreseterror);exit(EXIT_FAILURE);}}returnsem_id;}char*set_shm(key_tshm_key,intshm_num,i