银行排队系统.docx
- 文档编号:30182671
- 上传时间:2023-08-05
- 格式:DOCX
- 页数:21
- 大小:21.63KB
银行排队系统.docx
《银行排队系统.docx》由会员分享,可在线阅读,更多相关《银行排队系统.docx(21页珍藏版)》请在冰豆网上搜索。
银行排队系统
1、编程实现一个“银行排队模拟系统”
思想:
程序等待标准输入,若检测到有标准输入,则创建对应的进程。
如果目前等待态的进程个数等于座位数,在门外等待(即放入消息队列等待创建);若目前阻塞的进程个数小于座位数则进程阻塞。
与此同时,检测等待态的进程个数小于座位数,消息队列中存在消息,则从消息队列中取出一条消息,同时创建一个新进程。
本程序使用信号量进行进程间通信,使用消息队列存储从stdin输入的消息。
设置一信号量,标示大厅中座位。
设置一消息队列,标示在门外等待。
本程序,没有实现,老师所说的,“窗口进程”。
只是在主控进程中,对S3,S4进行了操作来实现与子进程的通信。
相当于,主控进程作为一个窗口进程,来处理各个客户进程。
由于,想要做到,在同一时间,主控进程最多只有两个子进程(客户进程)在执行,其他的都在阻塞状态,因此,本程序,将S3的初始值设置为2
1)该程序模拟客户到银行取号-排队-被叫号-被服务的过程;
2)程序执行流程如下:
Step1:
客户到达银行,并从取号机取号;
Step2:
如果大厅中有空闲座位,则座下等待,否则,在大厅外等待;
Step3:
银行职员如果发现有客户等待,则依次叫号服务,否则休息;
Step4:
step1-step4重复执行
3)大厅中座椅数量为20个;
4)服务窗口为2个;
5)“客户到来”通过命令行输入客户名字模拟;
6)为了模拟实际情况,每个客户服务时间不小于20秒,可随机确定;
7)程序顺序列出不同窗口服务客户的:
名称,窗口号,服务时间
2、提示
1)需一个主控进程,随时监控客户到来,并为之创建进程;
2)取号机应视为互斥型临界资源
3)座椅应视为临界资源
4)客户等待及被叫号应视为进程间同步过程
主控进程(serve.c)执行流程图:
客户端进程(client.c)执行流程图:
需求分析:
本系统,没有实现,窗口进程,最初目的在于,保证在同一时间,父进程即主控进程最多具有2个子进程在执行。
本模拟系统需要两个程序,一个作为主控进程,负责宏观调度(检测是否有标准输入,如果有,则创建子进程···);一个作为子进程,负责输出服务窗口(自己的PID),系统对自己的服务时间,自己等待系统服务的时间,(也可输出自己的名称,此处的传值方式与信号量的ID原理相同,因此省去本项)。
PS:
此程序以实现
serve.c为主控进程
client.c为子进程,子进程的二进制文件名必须为client,绝对路径必须为“mnt/usb/signal”如果想要修改,可以参看课本execle函数的使用守则
del.c如果主控进程异常退出但是有没有执行删除消息队列,信号量集合是使用,记得修改参数
serve.c
#include
#include
#include
#include
#include
#include
#include
#include
jmp_bufcrt;//设置跳转节点
jmp_bufend;
char*env_init[]={"USER=root","PATH=/mnt/usb",NULL};//初始化环境表中的信息
unionsemun//使用信号量函数,我们必须自己定义semun联合类型。
{
intval;
structsemid_ds*buf;
unsignedshort*array;
};
structmsgsbuf//定义向消息队列发送消息的结构体
{
intmtype;
charmtext[10];
}msg_sbuf;
structmsgmbuf//定义从消息队列接收消息的结构体
{
intmtype;
charmtext[10];
}msg_rbuf;
intcrt_msg(key_tkey)//创建一个消息队列,如果出错则跳转到出错处理程序,如果没有出错,则返回创建的消息队列的ID
{
intmsgid;
msgid=msgget(key,IPC_CREAT|00666);//如果已存在msgid这个值的消息队列,不阻塞,直接打开,如果不存在则根据key的值新建一个消息队列
//msgid=msgget(key,IPC_CREAT|IPC_EXCL|00666);//接受从主函数传递过来的KEY创建一个消息队列,此处设置为:
如果已存在该队列则出错返回
if(msgid<0)//判断是否出错
{
printf("createmessageerror!
\n")
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
returnmsgid;//返回创建的消息队列的ID
}
intcrt_sig(key_tkey,intnsems)//创建一个信号量集合,如果出错则跳转到出错处理程序,如果没有出错,则返回创建的信号量集合的ID
{
intsigid;
sigid=semget(key,nsems,IPC_CREAT|00666);//如果已存在msgid这个值的信号量集合,不阻塞,直接打开,如果不存在则根据key的值新建一个信号量集合
//sigid=semget(key,nsems,IPC_CREAT|IPC_EXCL|00666);//此处设置,如果已存在semid的信号量集合,则出错返回
if(sigid<0)//判断是否出错
{
printf("createsignalseterror!
\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
returnsigid;//返回创建的信号量集合的ID
}
pid_tcrt_child()//创建一子进程,如果出错,则跳转到setjmp(end)的下一句开始执行,如果没有出错,则返回新建子进程的ID
{
pid_tpid;
if((pid=fork())<0)//判断是否出错
{
printf("createchilderror!
\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
returnpid;//返回创建的子进程的ID
}
intmsg_stat(intmsqid,structmsqid_dsmsg_info)//查看消息队列的基本信息,本子函数也可用于测试对消息队列数据的传输是否正常,出错则跳转到setjmp(end)的下一句开始执行,返回消息队列成员的个数
{
intreval;
sleep
(1);
reval=msgctl(msqid,IPC_STAT,&msg_info);
if(reval<0)//判断是否出错
{
printf("getmessageinformatioinerror!
\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
returnmsg_info.msg_qnum;//返回消息队列成员的个数
}
intsig_blk_num(intsemid)//查看因等待信号量集合ID为semid的信号集中的第二个信号为1而阻塞的进程的个数,并将该个数返回
{
intreval;
reval=semctl(semid,2,GETNCNT);//获取因等待信号量集合ID为semid的信号集中的第二个信号为1而阻塞的进程的个数
returnreval;//返回因等待信号2而阻塞的进程个数
}
intget_sig_value(intsemid,intnum)//获取信号量集合ID为semid的集合的第num个信号的值,并将该值返回
{
intreval;
reval=semctl(semid,num-1,GETVAL);//获取函数
if(reval==-1)//判断是否出错
{
printf("getsignalset%dthmember'svalueerror!
\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
returnreval;//返回semid信号集合的第num个成员的值
}
voidsig_wait(intsemid,intnum)//对semid信号集合的第num个信号进行P(减一)操作,如果出错,则跳转到setjmp(end)的下一句开始执行,无返回值
{
printf("thisissig_waitsemid=%dnum=%d\n",semid,num);
intreval;
structsembufoperation;//定义sembuf结构体
operation.sem_num=num-1;//定义是semid信号集合的信号的下标,即对第num个信号进行操作
operation.sem_op=-1;//执行P操作(减一)
operation.sem_flg=SEM_UNDO;//如果不能对该信号进行操作(该资源已被占用),则阻塞,等待该信号的值为semop函数的第三个参数
reval=semop(semid,&operation,1);//具体操作函数
if(reval<0)//判断是否出错
{
printf("waitsignalerror!
\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
}
voidsig_post(intsemid,intnum)//对semid信号集合的第num个信号进行V(加一)操作,如果出错,则跳转到setjmp(end)的下一句开始执行,无返回值
{
intreval;
structsembufoperation;//定义sembuf结构体
operation.sem_num=num-1;//定义是semid信号量集合的下标,即对第num个信号进行操作
operation.sem_op=1;//执行V操作(加一)
operation.sem_flg=SEM_UNDO;//如果不能对该信号进行操作,则阻塞
reval=semop(semid,&operation,1);
if(reval<0)//判断是否出错
{
printf("postsignalerror!
\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
}
voidmsg_snd(intmsqid,constvoid*ptr,size_tnbytes)//向msqid消息发送*ptr所指向的结构体的消息,长度不大于nbytes
{
intreval;
reval=msgsnd(msqid,ptr,nbytes,IPC_NOWAIT);//发送,如果无法发送,则不等待直接返回
if(reval<0)//判断是否出错
{
printf("messagesenderror!
\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
}
voidmsg_rcv(intmsqid,void*ptr)//取消息队列msqid的消息放入*ptr所指向的结构体中
{
intreval;
reval=msgrcv(msqid,ptr,4,10,IPC_NOWAIT|MSG_NOERROR);
if(reval<0)//判断是否出错
{
printf("readmessageerror!
\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
else
printf("readfrommessagequeue%dbytes\n",reval);
}
voidsig_set(intsemid)//设置信号量集合中各个信号的初始值
{
unionsemunargument;//定义一个semun联合类型的结构体
unsignedshortvalues[4]={1,20,2,0};//定义一个短整型的数组,这个数组中的值对应要定义的信号量集合中各个信号的初始值
argument.array=values;//将短整型数组的值赋给semun联合类型结构体
intreval=semctl(semid,0,SETALL,argument);//semctl的第三个参数为setall表示根据argument结构体中的array中的值对semid中的信号量进行初始化
if(reval<0)//判断是否出错
{
printf("setmessagequeueerror!
\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
}
voiddel_msg(intmsqid)//删除msqid消息队列,如果出错,则跳转到setjmp(end)的下一句,开始执行
{
intreval;
reval=msgctl(msqid,IPC_RMID,NULL);//删除消息队列msqid即时生效
if(reval<0)//判断是否出错
{
printf("unlinkmessagequeueerror!
\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
}
voiddel_sig(intsemid)//删除semid信号量集合,如果出错,则跳转到setjmp(end)的下一句,开始执行
{
intreval;
reval=semctl(semid,0,IPC_RMID);//删除semid,即时生效
if(reval<0)//判断是否出错
{
printf("deletesignalseterror!
\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
}
voidexec_child(intsemid)//修改子进程的执行的程序段,如果出错,则跳转到setjmp(end)的下一句,开始执行
{
intreval;
reval=execle("/mnt/usb/signal/client","client",semid,(char*)0,env_init);//注意,此处使用的execle函数的第一个参数为子进程可执行文件的绝对路径,如果子进程的二进制文件不在该目录,则必定出错。
向子进程传递一semid(父进程创建的信号量集合的ID)
if(reval<0)//判断是否出错
{
printf("changechild'sprogramerror!
\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
}
intmain(intargc,char*argv[])
{
pid_tpid;
intreval;
intmsgid,msgnum;//消息队列ID,消息队列中成员的个数
intsigid,blknum;//信号量集合ID,因为等待S2而阻塞的进程个数
structmsqid_dsmsg_ginfo,msg_sinfo;//定义,消息队列结构体
structtmstim;//定义进城时间结构体
clock_ttime;
msgid=crt_msg(15);//创建key值为15的消息队列
printf("themessagequeue'sIDis%d\n",msgid);
sigid=crt_sig(51,4);//创建key值为51,成员个数为4的信号量集合
printf("thesignalset'sIDis%d\n",sigid);
sig_set(sigid);//初始化信号量集合中各个信号的初始值
printf("value1=%d\n",get_sig_value(sigid,1));//此处为测试代码,输出刚经过初始化的信号量集合中各个信号量的初始值
printf("value2=%d\n",get_sig_value(sigid,2));
printf("value3=%d\n",get_sig_value(sigid,3));
printf("value4=%d\n",get_sig_value(sigid,4));
intis;
intset=setjmp(end);//设置名字为“end”的跳转节点,第一次执行setjmp(end)返回的set值为0,如果是从longjmp(end,1)跳转过来的,则返回的set值为1
if(set==0)//longjmp(end,1)函数跳转过来,由此处开始执行,
is=1;
else
is=0;
while(is)//根据is的值判断是否出错,终止循环
{
printf("nowisinwhile!
\n");
time=times(&tim);//取有本程序所创建进程的时间结构体
if(time==-1)//判断是否出错
{
printf("getservetimeerror!
\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
else
if(tim.tms_stime>=300)//如果进程执行的时间>300
longjmp(end,1);//跳转到setjmp(end)进入终止处理的代码段。
printf("pleaseinputsomething\n");//提醒用户从标准输入输入
if(fgets(msg_sbuf.mtext,10,stdin)!
=NULL)//判断stdin(标准输入)是否有输入,如果有输入,则将输入的信息存放到结构体msg_sbuf的mtext中,由于定义的mtext的长度为10,所以,如果输入的字符串长度超过mtext的长度,系统会自动将该字符串截断为长度为10的字符串。
{
printf("inputsuccess!
\n");
if(fputs(msg_sbuf.mtext,stdout)==EOF)//判断将从stdin标准输入接收到的字符串输出到stdout标准输出是否出错
{
printf("stdouteorror!
\n");
}
//blknum=sig_blk_num(sigid);//因为等待S2而阻塞的自进程个数
blknum=20-get_sig_value(sigid,2);//查看椅子的占用情况
printf("nowblkprocess'snumberis%d\n",blknum);
if(blknum<20)//如果小于20
{
setjmp(crt);//创建一个crt跳转节点
printf("createchild\n");
pid=crt_child();//创建子进程
if(pid==0)//ifisinchild
{
exec_child(sigid);//修改子进程的镜像文件
}
printf("P(S3)\n");
sig_wait(sigid,3);//P(S3)
printf("V(S4)\n");
sig_post(sigid,4);//V(S4)
msgnum=msg_stat(msgid,msg_ginfo);//查看消息队列成员的个数
printf("nowmessagequeuehas%dmemeber\n",msgnum);
if(msgnum>0)//如果消息队列成员的个数大于0
{
printf("recivemessage\n");
msg_rcv(msgid,&msg_sbuf);//从消息队列中取消息
longjmp(crt,1);//跳转到setjmp(crt)语句的下一句
}
}
else//椅子已经占满,到门外等待
{
printf("chairhasallgone\nwaitoutthedoor\n");
msg_snd(msgid,msg_sbuf.mtext,sizeof(msg_sbuf.mtext));//向消息队列发送消息
msgnum=msg_stat(msgid,msg_ginfo);//此处为测试函数,查看消息队列中的成员个数
printf("nowtherehas%dmembersinqueue\n",msgnum);
}
}
}
printf("delmsganddelsig\n");
del_sig(sigid);//delsignalset
del_msg(msgid);//delmessagequeue
exit(0);
}
Client.c
#include
#include
#include
#include
#include
unionsemun
{
intval;
structsemid_ds*buf;
unsignedshort*array;
};
voidsig_wait(intsemid,intnum)//对semid信号集合的第num个信号进行P操作
{
intreval;
structsembufoperation;
operation.sem_num=num-1;//第num个信号
operation.sem_op=-1;//P操作
operation.sem_flg=SEM_UNDO;//如果该资源被占用,无法使用,则阻塞等待
reval=semop(semid,&operation,1);//操作
if(reval<0)//判断是否出错
{
printf("waitsignalerror!
\n");
exit
(1);//退出子进程
}
}
voids
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 银行 排队 系统