山大操作系统实验5.docx
- 文档编号:27587843
- 上传时间:2023-07-03
- 格式:DOCX
- 页数:26
- 大小:1.18MB
山大操作系统实验5.docx
《山大操作系统实验5.docx》由会员分享,可在线阅读,更多相关《山大操作系统实验5.docx(26页珍藏版)》请在冰豆网上搜索。
山大操作系统实验5
进程同步实验
张咪
软件四班
一、实验目的
总结和分析示例实验和独立实验中观察到的调试和运行信息,说明您对与解决非对称性互斥操作的算法有哪些新的理解和认识?
为什么会出现进程饥饿现象?
本实验的饥饿现象是怎样表现的?
怎样解决并发进程间发生的饥饿现象?
您对于并发进程间使用消息传递解决进程通信问题有哪些新的理解和认识?
根据实验程序、调试过程和结果分析写出实验报告。
二、实验要求
理发店问题:
假设理发店的理发室中有3个理发椅子和3个理发师,有一个可容纳4个顾客坐等理发的沙发。
此外还有一间等候室,可容纳13位顾客等候进入理发室。
顾客如果发现理发店中顾客已满(超过20人),就不进入理发店。
在理发店内,理发师一旦有空就为坐在沙发上等待时间最长的顾客理发,同时空出的沙发让在等候室中等待时间最长的的顾客就坐。
顾客理完发后,可向任何一位理发师付款。
但理发店只有一本现金登记册,在任一时刻只能记录一个顾客的付款。
理发师在没有顾客的时候就坐在理发椅子上睡眠。
理发师的时间就用在理发、收款、睡眠上。
请利用linux系统提供的IPC进程通信机制实验并实现理发店问题的一个解法。
三、实验环境
实验环境均为Linux操作系统,开发工具为gcc和g++。
四、实验思路
约束:
1.设置一个count变量来对顾客进行计数,该变量将被多个顾客进程互斥地访问并修改,通过一个互斥信号量mutext来实现。
count>20时,就不进入理发店。
7 然后等待申请沙发,用一个sofa信号量控制。 然后申请椅子。 3 count<3时,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(count>20){//顾客大于20人 signal(mutex); 离开理发店 } else{//顾客小于20人 count=count+1;//进入理发店 if(count>7){//count>7,说明理发椅和沙发上都有人,需要到等待室等待 signal(mutex); wait(room);//申请进入等待室 在等待室等 wait(sofa);//申请沙发 signal(room);//释放等待室 坐在沙发上等 wait(empty);//等待理发椅为空 申请到理发椅 signal(sofa);//释放沙发 } elseif(count>3){//说明理发椅上都有人,需要坐到沙发上等待 signal(mutex); wait(sofa);//申请沙发 坐在沙发上等 wait(empty);//等待理发椅为空 申请到理发椅 signal(sofa);//释放沙发 } else{//count<3,可以坐到理发椅上等待 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.h opts=-g-c c_src=cons.cipc.c c_obj=cons.oipc.o p_src=bar.cipc.c p_obj=bar.oipc.o all: producerconsumer consumer: $(c_obj) gcc$(c_obj)-oconsumer cons.o: $(c_src)$(hdrs) gcc$(opts)$(c_src) producer: $(p_obj) gcc$(p_obj)-oproducer bar.o: $(p_src)$(hdrs) gcc$(opts)$(p_src) clean: rmconsbar*.o 2.执行make命令,结果出现了许多由于粗心造成的编译错误 3、修改程序后编译成功,打开两个终端,先运行producer.c,再运行consumer.c 4、若按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: #include #include #include #include #include #include #include #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: #include"ipc.h" intget_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_id if((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,intshm_flg) { inti,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("shareMemoryseterror"); exit(EXIT_FAILURE); } //shmat将由shm_id标识的共享内存附加给指针shm_buf if((shm_buf=(char*)shmat(shm_id,0,0))<(char*)0) { perror("getshareMemoryerror"); exit(EXIT_FAILURE); } for(i=0;i } //shm_key标识的共享内存区已经建立,将由shm_id标识的共享内存附加给指针shm_buf if((shm_buf=(char*)shmat(shm_id,0,0))<(char*)0) { perror("getshareMemoryerror"); exit(EXIT_FAILURE); } returnshm_buf; } intset_msq(key_tmsq_key,intmsq_flg) { intmsq_id; //测试由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("messageQueueseterror"); exit(EXIT_FAILURE); } } returnmsq_id; } Bar.c: #include"ipc.h" intmain(intargc,char*argv[]) { //inti; intrate; Msg_bufmsg_arg; //可在在命令行第一参数指定一个进程睡眠秒数,以调解进程执行速度 if(argv[1]! =NULL)rate=atoi(argv[1]); elserate=3; //一个请求消息队列 wait_quest_flg=IPC_CREAT|0644; wait_quest_key=101; wait_quest_id=set_msq(wait_quest_key,wait_quest_flg); //一个响应消息队列 wait_respond_flg=IPC_CREAT|0644; wait_respond_key=102; wait_respond_id=set_msq(wait_respond_key,wait_respond_flg); //一个请求消息队列 sofa_quest_flg=IPC_CREAT|0644; sofa_quest_key=201; sofa_quest_id=set_msq(sofa_quest_key,sofa_quest_flg); //一个响应消息队列 sofa_respond_flg=IPC_CREAT|0644; sofa_respond_key=202; sofa_respond_id=set_msq(sofa_respond_key,sofa_respond_flg); //信号量使用的变量 costomer_key=301;//顾客同步信号灯键值 account_key=302;//账簿互斥信号灯键值 sem_flg=IPC_CREAT|0644; //顾客同步信号灯初值设为0 sem_val=0; //获取顾客同步信号灯,引用标识存costomer_sem costomer_sem=set_sem(costomer_key,sem_val,sem_flg); //账簿互斥信号灯初值设为1 sem_val=1; //获取消费者同步信号灯,引用标识存cons_sem account_sem=set_sem(account_key,sem_val,sem_flg); intpid1,pid2; pid1=fork(); if(pid1==0){ while (1){ //wait_quest_flg=IPC_NOWAIT; printf("%d号理发师睡眠\n",getpid()); wait_quest_flg=0; /*msgrcv()可以从消息队列中读取消息,msgsnd()将一个新的消息写入队列 msgtyp等于0则返回队列的最早的一个消息。 msgtyp大于0,则返回其类型为mtype的第一个消息。 msgtyp小于0,则返回其类型小于或等于mtype参数的绝对值的最小的一个消息。 */ if(msgrcv(sofa_quest_id,&msg_arg,sizeof(msg_arg),0,wait_quest_flg)>=0){//读沙发请求队列 msgsnd(sofa_respond_id,&msg_arg,sizeof(msg_arg),0);//往沙发回应队列里写 printf("%d号理发师为%d号顾客理发……\n",getpid(),msg_arg.mid); sleep(rate); down(account_sem); printf("%d号理发师收取%d号顾客交费\n",getpid(),msg_arg.mid); up(account_sem); } } }else{ pid2=fork(); if(pid2==0){ while (1){ //wait_quest_flg=IPC_NOWAIT; printf("%d号理发师睡眠\n",getpid()); wait_quest_flg=0; if(msgrcv(sofa_quest_id,&msg_arg,sizeof(msg_arg),0,wait_quest_flg)>=0){ msgsnd(sofa_respond_id,&msg_arg,sizeof(msg_arg),0); printf("%d号理发师为%d号顾客理发……\n",getpid(),msg_arg.mid); sleep(rate); down(account_sem); printf("%d号理发师收取%d号顾客交费\n",getpid(),msg_arg.mid); up(account_sem); }else{ printf("%d号理发师睡眠\n",getpid()); } } }else{ while (1){ //wait_quest_flg=IPC_NOWAIT; printf("%d号理发师睡眠\n",getpid()); wait_quest_flg=0; if(msgrcv(sofa_quest_id,&msg_arg,sizeof(msg_arg),0,wait_quest_flg)>=0){ msgsnd(sofa_respond_id,&msg_arg,sizeof(msg_arg),0); printf("%d号理发师为%d号顾客理发……\n",getpid(),msg_arg.mid); sleep(rate); down(account_sem); printf("%d号理发师收取%d号顾客交费\n",getpid(),msg_arg.mid); up(account_sem); }else{ printf("%d号理发师睡眠\n",getpid()); } } } } return0; } Cons.c: #include"ipc.h" intmain(intargc,char*argv[]) { intrate; Msg_bufmsg_arg; //可在在命令行第一参数指定一个进程睡眠秒数,以调解进程执行速度 if(argv[1]! =NULL)rate=atoi(argv[1]); elserate=3; //联系一个请求消息队列 wait_quest_flg=IPC_CREAT|0644; wait_quest_key=101; wait_quest_id=set_msq(wait_quest_key,wait_quest_flg); //联系一个响应消息队列 wait_respond_flg=IPC_CREAT|0644; wait_respond
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 操作系统 实验