山东大学操作系统实验五理发师问题报告Word格式文档下载.docx
《山东大学操作系统实验五理发师问题报告Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《山东大学操作系统实验五理发师问题报告Word格式文档下载.docx(18页珍藏版)》请在冰豆网上搜索。
![山东大学操作系统实验五理发师问题报告Word格式文档下载.docx](https://file1.bdocx.com/fileroot1/2023-1/28/fad00b6b-1a0a-473c-ab03-3f58477d57c4/fad00b6b-1a0a-473c-ab03-3f58477d57c41.gif)
每一个客户先判断sofa是不是坐满了,如
果没有就坐在沙发上等,否者就判断waitroom是不是坐满
了,如果没有,就坐在waitroom等,只要有一个坐在sofa
的客户离开sofa理发,理发师就会到waitroom找最先来的客户,让他进入sofa等待。
理发师程序思想:
理发师查看sofa上有没有人,没有就睡3秒,然后再一次
看有没有人,如果有人,就到沙发请最先来的客户来理发。
账本互斥的实现:
Semaphoremutex=1;
Sofa队列的长度和wait队列的长度的实现:
在顾客进程中设置两个变量sofa_count,wait_count,分别保存沙发和等候室的顾客数。
2、算法设计说明如下:
该解法利用消息队列的每条消息代表每个顾客,将进入等候室的顾客
组织到一个队列,将坐入沙发的顾客组织到另一个队列。
理发师从沙发队列请出顾客,空出的沙发位置再从等候室请入顾客进入沙发队列。
三个理发师进程使用相同的程序段上下文,所有顾客使用同一个
程序段上下文。
这样可避免产生太多进程,以便节省系统资源。
理发师程序(Barber)
{
建立一个互斥帐本信号量:
s_account,初值=1;
建立一个同步顾客信号量:
s_customer,初值=0;
建立沙发消息队列:
q_sofa;
建立等候室消息队列:
q_wait;
建立3个理发师进程:
b1_pid,b2_pid,b3_pid;
每个理发师进程作:
while
(1)
以阻塞方式从沙发队列接收一条消息,
如果有消息,则消息出沙发队列(模拟一顾客理发);
唤醒顾客进程(让下一顾客坐入沙发)。
用进程休眠一个随机时间模拟理发过程。
理完发,使用帐本信号量记账。
互斥的获取账本
记账
唤醒用账本理发师者
否则没有消息(沙发上无顾客)
则理发师进程在沙发队列上睡眠;
当沙发队列有消息时被唤醒(有顾客坐入沙发)。
}
顾客程序(customer)
取沙发队列消息数(查沙发上顾客数);
如果消息数小于4(沙发没座满)
以非阻塞方式从等候室队列接收一条消息(查等候室有顾客否),
如果有消息将接收到的消息发送到沙发队列(等候室顾客坐入沙发);
否则发送一条消息到沙发队列(新来的顾客直接坐入沙发);
否则(沙发坐满)
取等候室队列消息数(查等候室顾客数);
如果消息数小于13
发送一条消息到等候室队列(等候室没满,新顾客进等候室);
否则在顾客同步信号量上睡眠(等候室满暂不接待新顾客);
用进程休眠一个随机时间模拟顾客到达的时间间隔。
3、开发调试过程:
在shell命令行下运行$makebarbercustomer
gcc-g-cbarber.cipc.c
gccbarber.oipc.o-obarber
gcc-g-ccustomer.cipc.c
gcccustomer.oipc.o-ocustomer
假设先运行理发师程序:
$./barber
2726号理发师睡眠
2728号理发师睡眠
2727号理发师睡眠
运行$./customer
1号新顾客坐入沙发
2号新顾客坐入沙发
3号新顾客坐入沙发
4号新顾客坐入沙发
5号新顾客坐入沙发
6号新顾客坐入沙发
7号新顾客坐入沙发
8号新顾客坐入沙发
9号新顾客坐入沙发
10号新顾客坐入沙发
11号新顾客坐入沙发
12号新顾客坐入沙发
沙发坐满13号顾客在等候室等候
13号顾客从等候室坐入沙发
沙发坐满14号顾客在等候室等候
14号顾客从等候室坐入沙发
沙发坐满15号顾客在等候室等候
15号顾客从等候室坐入沙发
沙发坐满16号顾客在等候室等候
16号顾客从等候室坐入沙发
17号新顾客坐入沙发
沙发坐满18号顾客在等候室等候
18号顾客从等候室坐入沙发
沙发坐满19号顾客在等候室等候
19号顾客从等候室坐入沙发
沙发坐满20号顾客在等候室等候
20号顾客从等候室坐入沙发
沙发坐满21号顾客在等候室等候
21号顾客从等候室坐入沙发
在理发师窗体理发师进程被唤醒:
2726号理发师为1号顾客理发…
2726号理发师收取1号顾客交费
2728号理发师为2号顾客理发…
2728号理发师收取2号顾客交费
2727号理发师为3号顾客理发…
2726号理发师为4号顾客理发…
2727号理发师收取3号顾客交费
2726号理发师收取4号顾客交费
2728号理发师为5号顾客理发…
2728号理发师收取5号顾客交费
2727号理发师为6号顾客理发…2726号理发师为7号顾客理发
2727号理发师收取6号顾客交费2727号理发师睡眠
2726号理发师收取7号顾客交费
2728号理发师为8号顾客理发…
2728号理发师收取8号顾客交费
反之,如果先运行顾客程序:
$./customer
沙发坐满5号顾客在等候室等候沙发坐满6号顾客在等候室等候沙发坐满7号顾客在等候室等候沙发坐满8号顾客在等候室等候沙发坐满9号顾客在等候室等候沙发坐满10号顾客在等候室等候沙发坐满11号顾客在等候室等候沙发坐满12号顾客在等候室等候沙发坐满13号顾客在等候室等候
沙发坐满14号顾客在等候室等候沙发坐满15号顾客在等候室等候
沙发坐满17号顾客在等候室等候
等候室满18号顾客没有进入理发店
当18号顾客到达时理发店20个位置已满,顾客进程阻塞(假设理发
师进程没运行表示三个理发师正坐在3个理发椅上睡觉)。
再运行理发师程序:
运行截图如下:
4.7.分析与感悟:
首先运行顾客程序的话,顾客程序首先向沙发队列发送消息,然后向
等候室队列发送消息,当两个队列都满了之后,该进程会暂停,及停止在顾客同步信号量上。
而随着理发师程序的开始运行,理发师进程会唤醒顾客进程,及在顾客同步信号量上进行up操作,并且从消息
队列中接受消息。
反之,若理发师程序先运行,则三个理发师由于无法从沙发队列上接收到消息,而且由于是阻塞式接受,就会阻塞在这个消息队列上,只有当顾客程序运行时,向沙发队列发送消息后理发师进程才会继续。
通过编写这个实验,是我更加熟练了信号量的使用,明白了消息队列的使用方法,进一步了解了Linux系统中IPC进程同步工具的用法。
附件:
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;
i=atoi(colum);
fclose(pf);
returni;
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)
buf.sem_op=1;
uperror"
intset_sem(key_tsem_key,intsem_val,intsem_flg)
intsem_id;
Sem_unssem_arg;
〃测试由sem_key标识的信号灯数组是否已经建立
if((sem」d=get_ipc_id("
/proc/sysvipc/sem"
sem_key))<
0){
//semget新建一个信号灯,其标号返回到sem_id
if((sem」d=semget(sem_key,1,sem_flg))<
0)
semaphorecreateerror"
//设置信号灯的初值
sem_arg.val=sem_val;
if(semctl(sem」d,0,SETVAL,sem_arg)<
0)
semaphoreseterror"
returnsem」d;
char*set_shm(key_tshm_key,intshm_num,intshm_flg)
inti,shm」d;
char*shm_buf;
〃测试由shm_key标识的共享内存区是否已经建立
if((shm」d=get_ipc_id("
/proc/sysvipc/shm"
shm_key))<
0)
//shmget新建一个长度为shm_num字节的共享内存,其标
号返回到shm」d
if((shm」d=shmget(shm_key,shm_num,shm_flg))<
shareMemoryseterror"
//shmat将由shm」d标识的共享内存附加给指针shm_buf
if((shm_buf=(char*)shmat(shm_id,0,0))<
(char*)0)
getshareMemoryerror"
for(i=0;
i<
shm_num;
i++)shm_buf[i]=0;
//初始为0
〃shm_key标识的共享内存区已经建立,将由shm」d标识的
共享内存附加给指针shm_buf
returnshm_buf;
intset_msq(key_tmsq_key,intmsq_flg)
intmsq」d;
〃测试由msq_key标识的消息队列是否已经建立
if((msq_id=get_ipc_id("
/proc/sysvipc/msg"
msq_key))<
//msgget新建一个消息队列,其标号返回到msq_id
if((msq」d=msgget(msq_key,msq_flg))<
0){
messageQueueseterror"
returnmsq」d;
Ipc・h:
#include<
stdio.h>
stdlib.h>
sys/types.h>
sys/ipc.h>
sys/shm.h>
sys/sem.h>
sys/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);
Barber.c:
#inelude"
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」d=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;
pid仁fork();
if(pid1==0){
while
(1){
//wait_quest_flg=IPC_NOWAIT;
printf("
%d号理发师睡眠\n"
getpid());
wait_quest_flg=O;
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);
%d号理发师为%d号顾客理发……\n"
getpid(),msg_arg.mid);
sleep(rate);
down(account_sem);
printf("
%d号理发师收取%d号顾客交费\n"
up(account_sem);
}else{
pid2=fork();
if(pid2==0){
wait_quest_flg=O;
msg_arg,sizeof(msg_arg),
0,wait_quest_flg)>
msgsnd(sofa_respond_id,
&
msg_arg,sizeof(msg_arg),0);
%d号理发师为%d号顾客理发……\n"
%d号理发师收取%d号顾客交费\n"
return0;
Customer.c
intmain(intarge,ehar*argv[])
//联系一个请求消息队