银行排队系统
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<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/times.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/sem.h>
#include<setjmp.h>
jmp_buf crt;//设置跳转节点
jmp_buf end;
char *env_init[]={"USER=root","PATH=/mnt/usb",NULL};//初始化环境
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
中的信息
union semun//使用信号量函数,我们必须自己定义 semun 联合类型。
{
}; int val; struct semid_ds *buf; unsigned short *array;
struct msgsbuf//定义向消息队列发送消息的结构体
{
int mtype; char mtext[10];
}msg_sbuf;
struct msgmbuf//定义从消息队列接收消息的结构体
{
int mtype; char mtext[10];
}msg_rbuf;
int crt_msg(key_t key)//创建一个消息队列,如果出错则跳转到出错处理程序,如果没有出错,则返回创建的消息队列的ID {
int msgid; msgid=msgget(key,IPC_CREAT|00666);//如果已存在msgid这个值的消息队列,不阻塞,直接打开,如果不存在则根据key的值新建一个消息队列
//msgid=msgget(key,IPC_CREAT|IPC_EXCL|00666);//接受从主函数传递过来的KEY创建一个消息队列,此处设置为:如果已存在该队列则出错返回
if(msgid<0)//判断是否出错 {
printf("create message error!\n") longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
int crt_sig(key_t key,int nsems)//创建一个信号量集合,如果出错则跳转到出错处理程序,如果没有出错,则返回创建的信号量集合的ID
{
int sigid;
sigid=semget(key,nsems,IPC_CREAT|00666);//如果已存在msgid这个值的信号量集合,不阻塞,直接打开,如果不存在则根据key的值新建一个信号量集合
//sigid=semget(key,nsems,IPC_CREAT|IPC_EXCL|00666);} return msgid;//返回创建的消息队列的ID //此处设置,如果已存在semid的信号量集合,则出错返回
if(sigid<0)//判断是否出错 { printf("create signal set error!\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
} return sigid;//返回创建的信号量集合的ID
pid_t crt_child()//创建一子进程,如果出错,则跳转到setjmp(end)的下一句开始执行,如果没有出错,则返回新建子进程的ID {
pid_t pid; if((pid=fork())<0)//判断是否出错 { printf("create child error!\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
int msg_stat(int msqid,struct msqid_ds msg_info)//查看消息队列的基本信息,本子函数也可用于测试对消息队列数据的传输是否正常,出错则跳转到setjmp(end)的下一句开始执行,返回消息队列成员的个数
{
int reval; sleep(1); reval=msgctl(msqid,IPC_STAT,&msg_info); } return pid;//返回创建的
子进程的ID
if(reval<0)//判断是否出错 { printf("get message informatioin error!\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
int sig_blk_num(int semid)//查看因等待信号量集合ID为semid的信号集中的第二个信号为1而阻塞的进程的个数,并将该个数返回
{
int reval; reval=semctl(semid,2,GETNCNT);//获取因等待信号量集合} return msg_info.msg_qnum;//返回消息队列成员的个数 ID为semid的信号集中的第二个信号为1而阻塞的进程的个数
}
int get_sig_value(int semid,int num)//获取信号量集合ID为semid的集合的第num个信号的值,并将该值返回
{
int reval; reval=semctl(semid,num-1,GETVAL);//获取函数 return reval;//返回因等待信号2而阻塞的进程个数
if(reval==-1)//判断是否出错 { printf("get signal set %dth member’s value
error!\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
void sig_wait(int semid,int num)//对semid信号集合的第num个信号进行P(减一)操作,如果出错,则跳转到setjmp(end)的下一句开始执行,无返回值
{
printf("this is sig_wait semid=%d } return reval;//返回semid信号集合的第num个成员的值 num=%d\n",semid,num);
int reval; struct sembuf operation;//定义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("wait signal error!\n"); longjmp(end,1);//如果出错,跳转到setjmp(end)
语句的下一句,开始执行程序。
}
void sig_post(int semid,int num)//对semid信号集合的第num个信号进行V(加一)操作,如果出错,则跳转到setjmp(end)的下一句开始执行,无返回值
{
int reval; struct sembuf operation;//定义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("post signal error!\n"); longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
void msg_snd(int msqid,const void *ptr,size_t nbytes)//向msqid消息发送*ptr所指向的结构体的消息,长度不大于nbytes {
int reval; reval=msgsnd(msqid,ptr,nbytes,IPC_NOWAIT);//发送,如} 果无法发送,则不等待直接返回
if(reval<0)//判断是否出错 { printf("message send error!\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
void msg_rcv(int msqid,void *ptr)//取消息队列msqid的消息放入*ptr所指向的结构体中
{ }
int reval; reval=msgrcv(msqid,ptr,4,10,IPC_NOWAIT|MSG_NOERROR);
if(reval<0)//判断是否出错 { printf("read message error!\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
void sig_set(int semid)//设置信号量集合中各个信号的初始值 {
体
unsigned short values[4]={1,20,2,0};//定义一个短整型的数union semun argument;//定义一个
semun 联合类型的结构} else printf("read from message queue %d bytes\n",reval);
组,这个数组中的值对应要定义的信号量集合中各个信号的初始值 argument.array=values;//将短整型数组的值赋给semun 联合类型结构体
int reval=semctl(semid,0,SETALL,argument);//semctl的第三个参数为setall表示根据
argument结构体中的array中的值
对semid中的信号量进行初始化
if(reval<0)//判断是否出错 { printf("set message queue error!\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
void del_msg(int msqid)//删除msqid消息队列,如果出错,则跳转到setjmp(end)的下一句,
开始执行
{
int reval; reval=msgctl(msqid,IPC_RMID,NULL);//删除消息队列} msqid即时生效
if(reval<0)//判断是否出错 { printf("unlink message queue error!\n");
longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
void del_sig(int semid)//删除semid信号量集合,如果出错,则}
跳转到setjmp(end)的下一句,开始执行
{
int reval; reval=semctl(semid,0,IPC_RMID);//删除semid,即时生效 if(reval<0)//判断是否出
错 { printf("delete signal set error!\n"); longjmp(end,1);//如果出错,跳转到
setjmp(end)语句的下一句,开始执行程序。
}
void exec_child(int semid)//修改子进程的执行的程序段,如果出错,则跳转到setjmp(end)的下一句,开始执行
{
int reval; reval=execle("/mnt/usb/signal/client","client",semid,(c} har
*)0,env_init);//注意,此处使用的execle函数的第一个参数为子进程可执行文件的绝对路径,
如果子进程的二进制文件不在该目录,则必定出错。向子进程传递一semid(父进程创建的
信号量集合的ID)
if(reval<0)//判断是否出错 {
printf("change child’s program error!\n"); longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
}
int main(int argc,char *argv[])
{
pid_t pid; int reval; int msgid,msgnum;//消息队列ID,消息队列中成员的个数 int sigid,blknum;//信号量集合ID,因为等待S2而阻塞的进程} 个数
struct msqid_ds msg_ginfo,msg_sinfo;//定义,消息队列结构体
struct tms tim;//定义进城时间结构体 clock_t time; msgid=crt_msg(15);//创建key值为15的消息队列 printf("the message queue’s ID is %d\n",msgid); sigid=crt_sig(51,4);//创建key值为51,成员个数为4的信号量集合
printf("the signal set’s ID is %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)); int is; int set=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("now is in while!\n");
time=times(&tim);//取有本程序所创建进程的时间结构体 if(time==-1)//判断是否出错 { printf("get serve time error!\n"); longjmp(end,1);//如果出错,跳转到setjmp(end)语句的下一句,开始执行程序。
} else if(tim.tms_stime>=300)//如果进程执行的时间>300 longjmp(end,1);//跳转到setjmp(end)进入终止处理的代码段。
printf("please input something\n");//提醒用户从标准输入输入
if(fgets(msg_sbuf.mtext,10,stdin)!=NULL)//判断stdin(标准输入)是否有输入,如果有输入,则将输入的信息存放到结构体msg_sbuf的mtext中,由于定义的mtext的长度为10,所以,如果输入的字符串长度超过mtext的长度,系统会自动将该字符串截断为长度为10的字符串。
{ printf("input success!\n"); if(fputs(msg_sbuf.mtext,stdout)==EOF)//判断将从stdin标准输入接收到的字符串输出到stdout标准输出是否出错
{ } //blknum=sig_blk_num(sigid);//因为等待S2而阻塞printf("stdout eorror!\n"); 的自进程个数
blknum=20-get_sig_value(sigid,2);//查看椅子的占
用情况
printf("now blk process’s number is %d\n",blknum);
if(blknum<20)//如果小于20 { setjmp(crt);//创建一个crt跳转节点
printf("create child\n"); pid=crt_child();//创建子进程 if(pid==0)//if is in child { } 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);//查看消exec_child(sigid);//修改
子进程的镜像文件 息队列成员的个数
printf("now message queue has %d memeber\n",msgnum);
if(msgnum>0)//如果消息队列成员的个数大于0 {
printf("recive message\n"); msg_rcv(msgid,&msg_sbuf);//从消息队列中取消
息
longjmp(crt,1);//跳转到setjmp(crt)语句的下一句
} else//椅子已经占满,到门外等待 { printf("chair has all gone\nwait out the }
door\n");
msg_snd(msgid,msg_sbuf.mtext,sizeof(msg_sbuf.mtext));//向消息队列发送消息
msgnum=msg_stat(msgid,msg_ginfo);//此处为测试函数,查看消息队列中的成员个数
printf("now there has %d members in queue\n",msgnum);
} } }
printf("del msg and del sig\n");
del_sig(sigid);//del signal set
del_msg(msgid);//del message queue
exit(0);
}
Client.c
#include<stdio.h>
#include<stdlib.h>
#include<sys/times.h>
#include<sys/ipc.h>
#include<sys/sem.h>
union semun
{
};
void sig_wait(int semid,int num)//对semid信号集合的第num个信号进行P操作
{
int reval; struct sembuf operation; operation.sem_num=num-1;//第num个信号 int val; struct
semid_ds *buf; unsigned short *array;
operation.sem_op=-1;//P操作 operation.sem_flg=SEM_UNDO;//如果该资源被占用,无法使用,则阻塞等待
}
void sig_post(int semid,int num)//对semid信号集合的第num个信号进行V操作
{
int reval; struct sembuf operation;//定义sembuf结构体 operation.sem_num=num-1;//第num个信号 operation.sem_op=1;//V操作 operation.sem_flg=SEM_UNDO;//如果该资源被占用,无法reval=semop(semid,&operation,1);//操作 if(reval<0)//判断是否出错 { } printf("wait signal error!\n"); exit(1);//退出子进程 使用,则阻塞等待,对于V操作无实质意义,不会阻塞。
reval=semop(semid,&operation,1);//操作 if(reval<0)//判断是否出错 {
} } printf("post signal error!\n"); exit(1);//退出子进程
int main(int argc,char *argv[])
{
int sigid=(int)argv[1];//从命令行接受从父进程传递过来的semid,因为从命令行接收到的为char型,所以此处需要强制类型转换
sig_wait(sigid,1);//P(S1) sig_post(sigid,1);//V(S1) sig_wait(sigid,2);//P(S2)
sig_post(sigid,3);//V(S3) sig_wait(sigid,4);//P(S4) sig_post(sigid,2);//V(S2) int i; pid_t pid;
struct tms time_buf; clock_t time; pid=getpid();//获得当前进程的PID if(pid==-1)//判断获得PID是否成功
{ } time=times(&time_buf);//获取子进程的时间结构体 if(time==-1)//判断获取是否成功 { } printf("get serve time error!\n"); exit(1);//退出子进程 printf("get
client’s pid error!\n"); exit(1);//退出子进程
// printf("client’s name is %s\n",argv[1]);
printf("server window’s id is %d\n",pid);//输出当前进程的PID
printf("serve time is %d\n",time_buf.tms_stime);//输出当前进程的系统服务时间
printf("wait time is %d\n",time_buf.tms_utime);//输出当前进程的用户等待时间
}
Del.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/times.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<sys/sem.h>
#include<setjmp.h>
void del_msg(int msqid)//删除消息队列
{
int reval; reval=msgctl(msqid,IPC_RMID,NULL);//删除msqid消息队列
}
void del_sig(int semid)//删除信号量集合
{
int reval; reval=semctl(semid,0,IPC_RMID);//删除semid信号量集合 if(reval<0)
if(reval<0) { } printf("unlink message queue error!\n"); exit(1);//退出进程
} { } printf("delete signal set error!\n"); exit(1);//退出进程
int main(int argc,char *argv[]) {
}
del_msg(msgid);//此处应该是serve输出的msgid的值 del_sig(sigid);//此处应该是serve输出的sigid的值