山东大学软件学院操作系统实验报告实验题目:进程互斥实验实验目的:进一步研究和实践操作系统中关于并发进程同步与互斥操作的一些经典问题的解法,加深对于非对称性互斥问题有关概念的理解。观察和体验非对称性互斥问题的并发控制方法。进一步了解Linux系统中IPC进程同步工具的用法,训练解决对该类问题的实际编程、调试和分析问题的能力。实验要求:理发店问题:假设理发店的理发室中有3个理发椅子和3个理发师,有一个可容纳4个顾客坐等理发的沙发。此外还有一间等候室,可容纳13位顾客等候进入理发室。顾客如果发现理发店中顾客已满(超过20人),就不进入理发店。在理发店内,理发师一旦有空就为坐在沙发上等待时间最长的顾客理发,同时空出的沙发让在等候室中等待时间最长的的顾客就坐。顾客理完发后,可向任何一位理发师付款。但理发店只有一本现金登记册,在任一时刻只能记录一个顾客的付款。理发师在没有顾客的时候就坐在理发椅子上睡眠。理发师的时间就用在理发、收款、睡眠上。请利用linux系统提供的IPC进程通信机制实验并实现理发店问题的一个解法。总结和分析示例实验和独立实验中观察到的调试和运行信息,说明您对与解决非对称性互斥操作的算法有哪些新的理解和认识?为什么会出现进程饥饿现象?本实验的饥饿现象是怎样表现的?怎样解决并发进程间发生的饥饿现象?您对于并发进程间使用消息传递解决进程通信问题有哪些新的理解和认识?根据实验程序、调试过程和结果分析写出实验报告。硬件环境:CPU:P4/1.8MHz内存:256MB硬盘:10GB软件环境:Ubuntu08.4-Linux操作系统Gnome桌面2.18.3BASH_VERSION='3.2.33(1)-releasegccversion4.1.2vi3.1.2gedit2.18.2OpenOffice2.3实验步骤:1.问题分析假设理发店的理发室中有3个理发椅子和3个理发师,有一个可容纳4个顾客坐等理发的沙发。此外还有一间等候室,可容纳13位顾客等候进入理发室。顾客如果发现理发店中顾客已满(超过20人),就不进入理发店。在理发店内,理发师一旦有空就为坐在沙发上等待时间最长的顾客理发,同时空出的沙发让在等候室中等待时间最长的的顾客就坐。顾客理完发后,可向任何一位理发师付款。但理发店只有一本现金登记册,在任一时刻只能记录一个顾客的付款。理发师在没有顾客的时候就坐在理发椅子上睡眠。理发师的时间就用在理发、收款、睡眠上。2.算法设计说明该解法利用消息队列的每条消息代表每个顾客,将进入等候室的顾客组织到一个队列,将坐入沙发的顾客组织到另一个队列。理发师从沙发队列请出顾客,空出的沙发位置再从等候室请入顾客进入沙发队列。三个理发师进程使用相同的程序段上下文,所有顾客使用同一个程序段上下文。这样可避免产生太多进程,以便节省系统资源。intsem_p(intsemid,intindex,intipc_nowait){//P操作structsembufbuf={0,-1,0};buf.sem_num=index;buf.sem_flg=ipc_nowait?IPC_NOWAIT:0;if(semop(semid,&buf,1)==-1){if(ipc_nowait&&errno==EAGAIN){return-2;}else{perror(awrongoperationtosemaphoreoccured!);return-1;}}return0;}intsem_v(intsemid,intindex,intipc_nowait){//V操作structsembufbuf={0,1,0};buf.sem_num=index;buf.sem_flg=ipc_nowait?IPC_NOWAIT:0;if(semop(semid,&buf,1)==-1){if(ipc_nowait&&errno==EAGAIN){return-2;}else{perror(awrongoperationtosemaphoreoccured!);return-1;}}return0;}Costumo:if((pid=fork())0){perror(forkerror!);exit(EXIT_FAILURE);}elseif(pid==0){//childprocesswhile(1){sem_p(wait_lock_semid,1,0);//等待沙发上有人sem_p(wait_semid,0,0);//等待理发师有空if(msg_get(wait_msg,&msg,WAIT_ID_SOFA,0)0){perror(messagegeterror!);exit(-1);}msg_send(wait_msg,WAIT_ID_CHAIR,msg.id,0);printf(theconsumer%d==chair,wait%dseconds\n,msg.id,(time(NULL)-msg.timestamp));//sofa==chairsem_v(wait_semid,1,0);//沙发上有空位了sem_v(wait_lock_semid,0,0);//通知有人来理发了}return0;}if((pid=fork())0){perror(forkerror!);exit(EXIT_FAILURE);}elseif(pid==0){//childprocesswhile(1){sem_p(wait_lock_semid,2,0);//等待室内有人sem_p(wait_semid,1,0);//沙发上有空位if(msg_get(wait_msg,&msg,WAIT_ID_ROOM,0)0){perror(messagegeterror!);exit(-1);}msg_send(wait_msg,WAIT_ID_SOFA,msg.id,0);printf(theconsumer%d==sofa,wait%dseconds\n,msg.id,(time(NULL)-msg.timestamp));//room==sofasem_v(wait_semid,2,0);//等待室空出1个位子sem_v(wait_lock_semid,1,0);//通知沙发上有人了}return0;}while(1){if((pid=fork())0){perror(forkerror!);exit(EXIT_FAILURE);}elseif(pid==0){//if((consumer_status=sem_p(wait_semid,2,1))==-2){//检查理发店(等待室)是否已满printf(我是顾客%d,理发店满了,我走人了。。。\n,getpid());//full}elseif(consumer_status==0){//仍然有空间printf(我是顾客%d,进入理发店\n,getpid());printf(我是顾客%d,正在等待室等待\n,getpid());msg_send(wait_msg,WAIT_ID_ROOM,getpid(),0);//进入等待室sem_v(wait_lock_semid,2,0);//通知等待室有人了}exit(0);}sleep(3);//}Baker:if(pid[2]==0){while(1){if(barber_status==0){if(sem_p(wait_lock_semid,0,1)==-2){//等待有人来理发或需要收银printf(我是%d号理发师,正在睡觉。。。zzzZZZ\n,barber_id);sem_p(wait_lock_semid,0,0);}if(sem_p(wait_lock_semid,3,1)==0){//如果是需要收银barber_status=2;if(msg_get(wait_msg,&msg,WAIT_ID_CASH,1)0){perror(message1geterror!);exit(-1);}barber_consumer=msg.id;}else{barber_status=1;if(msg_get(wait_msg,&msg,WAIT_ID_CHAIR,1)0){perror(message2geterror!);exit(-1);}barber_consumer=msg.id;}}elseif(barber_status==1){printf(我是%d号理发师,正在为%d进行理发。。。\n,barber_id,barber_consumer);sleep(15);barber_status=0;sem_v(wait_lock_semid,3,0);//需要收银msg_send(wait_msg,WAIT_ID_CASH,barber_consumer,0);sem_v(wait_lock_semid,0,0);}else{sem_p(cash_semid,0,0);//收银本的互斥锁printf(我是%d号理发师,正在为%d进行收银。。。\n,barber_id,barber_consumer);sem_v(cash_semid,0,0);barber_status=0;sem_v(wait_semid,0,0);//通知理发师有空了}}}else{signal(SIGINT,handler_exit);wait();}3.某种方法的伪代码理发师程序(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发送一条消息到等候室队列(等候室没满,新顾客进等候室);否则在顾客同步信号量上睡眠(等候室满暂不接待新顾客);用进程休眠一个随机时间模拟顾客到达的时间间隔。}}4.实验收获通过本次试验,使我加深了对进程互斥概念的理解,体验到了共享内存、信号灯数组以及消息队列的功能。基本已经掌握了如何用消息队列控制和堵塞进程,实现对共享内存的有序访问。另外,是我加深了对理发师算法的理解,找到了它与读者写者问题的共同之处:1.进程间的互斥2.理发师类似读者进程,顾客类似写者进程。最后,通过不断的调试,使我熟练了在Linux环境下编程的技巧,对进程的创建与控制更加熟悉。附录A:本实验全部程序源代码及注释IPC.H#ifndefCZW_IPC_H_INCLUDED#defineCZW_IPC_H_INCLUDED#includeerrno.h#includesys/types.h#includesys/sem.hintsem_create(constchar*pathname,intproj_id,intnsems,intinit_value){key_tkeyid;intsemid,i;if((keyid=ftok(pathname,proj_id))==-1){perror(ftokerror!);return-1;}if((semid=semget(keyid,ns