操作系统利用信号量实现银行叫号排队系统 课程设计实验报告

发布时间:2024-11-12

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

操作系统课程设计

题目:进程通信与进程同步机制实践

所在学院: 计算机学院 所在班级: 软件1201 学生姓名: 学生学号: 指导教师: 薛安荣

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

2015年 1 月

一、题目

某银行提供5个服务窗口(3个对私服务窗口,1个对公服务窗口,1个理财服务窗口)和10个供顾客等待的座位。顾客到达银行时,若有空座位,则到取号机上领取一个号,等待叫号;若没有空座位,则在门外等待或离开。取号机每次仅允许一位顾客使用,有对公、对私和理财三类号,每位顾客只能选取其中一个。当营业员空闲时,通过叫号选取一位顾客,并为其服务。请用P、V操作写出进程的同步算法。

二、目的

1、 掌握基本的同步与互斥算法 。

2、学习使用 Linux 中基本的同步对象,掌握相关 API 的使用方法。 3、了解 Linux 中多任务的并发执行机制,实现进程的同步与互斥 。

三、实验环境

Linux CentOS

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

编译器GCC 编程语言 C语言

四、要求

1、当有顾客取号的时候,不允许其他顾客取号。 2、当服务窗口满的情况下,其他人必须等待。 3、当没有顾客的情况下,服务窗口必须等待。 4、打印:A、 初始状态

B、 中间变化的状态信息 C、以及最终状态信息。

五、原理及算法

本程序中设计6个信号量,其中signal_A、signal_B和signal_C分别是对私、对公、理财窗口的同步信号量。若信号量值的等于0,说明当前没有空闲空口,顾客需要等待。另设置一个signal_seat同步信号量,记录当前的座位情况,若该信号量等于0,说明当前没有空座位,顾客需要等待。另有一个signal_customer同步信号量用于记录当前已经取过票的总人数,用于生成票号信息。还有一个mutex互斥信号量,用于实现各进程在对信号量进行操作时的互斥。

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

顾客进入银行之后,先看通过一个依据系统时间的随机数来确定自己是需要对私、对公还是理财服务(在本程序中分别对应于A类顾客,B类顾客和C类顾客),这三个类型的顾客的比例为3:1:1.然后顾客根据自己需要的服务类型,查看提供相应类型服务的窗口是否空闲,若窗口有空闲,则系统直接按照signal_customer记录的信息,生成票面信息;若窗口没有空闲,则再去查看signal_seat信号量看看是否有空座位,若有空座位,则根据signal_customer记录的信息,生成票面信息;若没有空座位,则通过一个以系统时间为种子的随机数生成器生成一个随机数,帮助顾客确定是要继续等待还是离开,这两种情况的比例为1:1.若顾客选择离开,则相应的进程退出。

当顾客取到票后,便开始查看对应类型的窗口是否有空闲,如果有空闲,则上前办理业务。顾客办理业务需要的时长通过以系统时间为种子的随机数生成器来确定,时长均在10秒到60秒之间。 在程序执行的过程中,顾客的状态每有变换,都会有相应的输出提示信息,并在输出的行尾输出发生该动作时当前的系统时间,以便于我们分析各个顾客进程的执行情况。

六、说明

本实验在Linux环境下完成,该程序是通过进程实现的。包含一个service可执行文件,一个customer可执行文件和一个deletesem可执行文件。其中service可执行文件用于在内存中申请一个共享内

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

存空间,并将这个内存空间与自身进程绑定;customer可执行文件每执行一次,就增加一个进程,即代表有一位顾客来到。通过多次执行customer可执行文件来模拟多位顾客;deletesem可执行文件用于在service进程和customer进城都执行完毕后,删除内存空间中的信号集。

实验中利用GCC编译器,通过编写Makefile文件来快速编译源代码生成以上三个可执行文件。

七、程序中各主要函数说明

1、int createshm( char * pathname, int proj_id, size_t size) 创建共享内存的函数,操作成功则返回共享内存标识符,失败返回-1.

2、int createsem (const char * pathname, int proj_id, int members, int init_val[])

在共享内训中创建信号量的函数,操作成功则返回信号集标识符,失败返回-1.

3、int opensem(const char * pathname, int proj_id)

打开信号集函数,操作成功则返回信号集标识符,失败返回-1. 4、int sem_p(int semid, int index)

P操作函数,操作成功则返回0,失败返回-1. 5、int sem_v(int semid, int index)

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

V操作函数,操作成功则返回0,失败返回-1. 6、int wait_sem(int semid, int index) 等待信号量为1函数,操作成功返回1. 7、int delete_sem (int semid) 删除信号集函数,操作成功返回0. 8、int get_sem_val (int semid ,int index)

获取指定信号集中指定下标的信号量的值,操作成功返回该信号量的值。

9、int my_random()

自定义随机数生成器,以系统时间为种子,每次执行前延时1秒,以便于生成不同的随机数。 10、void print_time() 打印当前系统时间。

11、int get_ticket(int semid, char identifyLabel, int * ticket, int * flag) 顾客取票函数,取票成功,返回1,失败返回-1,,由于没有空座,顾客选择离开则返回0。

12、void service(int semid, char identifyLabel, int * ticket, int *flag) 顾客办理业务函数。

八、源程序清单

sharemem.h文件

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/sem.h> #include<sys/shm.h> #include<errno.h> #include<time.h>

#define SHM_SIZE 1024

#define INDEX_MUTEX 0 #define INDEX_SIGNAL_SEAT 1 #define INDEX_SIGNAL_CUSTOMER 2 #define INDEX_SIGNAL_A 3 #define INDEX_SIGNAL_B 4 #define INDEX_SIGNAL_C 5

/*定义信号量*/

/*同步信号量一共有4个*/

int signal_A=3, signal_B=1, signal_C=1, signal_seat=3, signal_customer=0; /*互斥信号量mutex控制进程对每个同步信号量的操作*/ int mutex=1; int signal_count=6;

union semun { };

/*创建共享内存函数*/

int createshm( char * pathname, int proj_id, size_t size) {

//信号量计数器,记录一共有多少信号量

int val;

struct semid_ds * buf; unsigned short * array;

key_t shmkey; int sid; /*获取键值*/

if ((shmkey=ftok(pathname, proj_id))==-1) {

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

}

/*定义创建信号量的函数*/

int createsem (const char * pathname, int proj_id, int members, int init_val[]) {

}

exit(1); return -1;

if ((sid=shmget(shmkey, size, IPC_CREAT | 0666))==-1) { }

return (sid);

perror ("shmget call failed.\n"); exit(1); return -1;

key_t msgkey; int index,sid;

union semun semopts;

if ((msgkey=ftok(pathname, proj_id))==-1) { }

if((sid=semget(msgkey, members, IPC_CREAT | 0666))==-1) { }

/*对信号量进行初始化操作*/

for(index=0;index<members;index++) { }

semopts.val=init_val[index]; //根据init_val数组中的值对各个信号量进semctl (sid,index,SETVAL,semopts); perror ("semget call failed.\n"); exit(1); return -1;

perror ("ftok error!\n"); exit(1); return -1;

行初始化

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

}

/*打开信号量函数*/

int opensem(const char * pathname, int proj_id) { }

/*P操作函数*/

int sem_p(int semid, int index) {

return (sid);

key_t msgkey; int sid;

if ((msgkey=ftok(pathname,proj_id))==-1) { }

if((sid=semget(msgkey, 0 ,IPC_CREAT | 0666))==-1) { }

return (sid);

perror ("semget call failed.\n"); exit(1); return -1;

perror ("ftok error!\n"); exit(1); return -1;

struct sembuf buf={0,-1,IPC_NOWAIT}; if(index<0) { }

buf.sem_num=index;

if(semop (semid , & buf,1)==-1) {

perror("index of array cannot equals a minus value!"); exit(1); return (-1);

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

}

/*V操作函数*/

int sem_v(int semid, int index) { }

/*等待信号量为1函数*/

int wait_sem(int semid, int index) { }

/*删除信号集函数*/

}

exit(1); return -1;

return 0;

struct sembuf buf={0,+1,IPC_NOWAIT}; if(index<0) { }

buf.sem_num=index;

if(semop (semid , & buf,1)==-1) { } return 0;

perror ("a wrong operation to semaphore occurred!"); exit(1); return (-1);

perror("index of array cannot equals a minus value!"); exit(1); return (-1);

while(semctl(semid, index, GETVAL, 0)==0) { } return 1;

usleep(10000);

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

int delete_sem (int semid) { }

/*获取指信号量的值*/

int get_sem_val (int semid ,int index) { }

/*我的随机数生成器*/ int my_random() { }

/*获取当前系统时间并靠右输出*/ void print_time() { }

/*顾客取票函数,取票成功,返回1,失败返回-1,,由于没有空座,顾客选择离开则返回0*/ int get_ticket(int semid, char identifyLabel, int * ticket, int * flag) {

return (semctl(semid, 0 , IPC_RMID));

return semctl(semid, index, GETVAL, 0);

usleep(1000000); return (rand());

//延时函数,延时1秒,以便于生成不同的随机数

//初始化随机数生成器

//调用随机数生成器生成并返回随机数

srand((unsigned)time(NULL));

time_t now; time(&now);

//实例化time_t结构

//实例化tm结构指针

//time函数读取现在的时间(国际标准时间非北京时间),然后传值给

//localtime函数把从time取得的时间now换算成你电

struct tm *timenow;

now

timenow=localtime(&now); 脑中的时间(就是你设置的地区)

printf("Time:%s\n",asctime(timenow));

//asctime函数把时间转换成字符,通过

printf()函数靠右输出

int wait_num=0;

switch(identifyLabel) {

case 'A':

if(get_sem_val(semid, INDEX_SIGNAL_SEAT) == 0) {

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

}

return 0;

//返回0表示顾客选择离开

printf("目前没有空座位,但该顾客选择了等待。\n\n");

if(get_sem_val(semid, INDEX_SIGNAL_A) > 0) { }

if( wait_sem(semid, INDEX_SIGNAL_SEAT) )

sem_p(semid, INDEX_SIGNAL_SEAT);

*flag=0;

//检查,如果有空窗

口,则直接取票,不用判断座位

//将标志为设为0,标示没有顾客在等待,不用排队

//跳转到直接取票

//如果没有空座,则等

goto goto_A;

待空座

goto_A: if( wait_sem(semid, INDEX_MUTEX) )

sem_p(semid, INDEX_MUTEX);

//等待操作信号量释放

//锁定操作信号量

//

*ticket=get_sem_val(semid, INDEX_SIGNAL_CUSTOMER) + 1;

分配票号(票号是根据当天的总顾客数排的)

wait_num = (signal_seat - get_sem_val(semid,INDEX_SIGNAL_SEAT) ) -1;

printf("号码:A%03d,",*ticket); if(wait_num==0 || *flag==0) { } else { }

printf("顾客A%03d正在等待办理业务。\t",*ticket); print_time();

printf("有%d位顾客正在等待。\t",wait_num); print_time();

printf("当前没有顾客正在等待。\t"); print_time();

//wait_num大于等于0或者flag

//获取当前等待的人数

大于0,表示当前无人等待

if( sem_v(semid, INDEX_SIGNAL_CUSTOMER) !=0) {

printf("对不起,V操作失败!");

//记录目前办理

过业务和等待办理业务的顾客总数

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

}

return -1;

//释放控制进程访问资源的信号量

sem_v(semid, INDEX_MUTEX); break;

case 'B':

if(get_sem_val(semid, INDEX_SIGNAL_SEAT) == 0) { }

if(get_sem_val(semid, INDEX_SIGNAL_B) > 0) { }

if( wait_sem(semid, INDEX_SIGNAL_SEAT) )

*ticket=get_sem_val(semid, INDEX_SIGNAL_CUSTOMER) + 1;

//

sem_p(semid, INDEX_SIGNAL_SEAT);

//等待操作信号量释放

sem_p(semid, INDEX_MUTEX);

//锁定操作信号量

//如果没有空座,则等

*flag=0;

//将标志为设为0,标示没有顾客在等待,不用排队

//跳转到直接取票

goto goto_B;

//检查,如果有空窗

if( (wait_num=my_random()%10)<5)

return 0;

//返回0表示顾客选择离开

printf("目前没有空座位,但该顾客选择了等待。\n\n");

口,则直接取票,不用判断座位

待空座

goto_B: if( wait_sem(semid, INDEX_MUTEX) )

分配票号(票号是根据当天的总顾客数排的)

wait_num = (signal_seat - get_sem_val(semid,INDEX_SIGNAL_SEAT) ) -1;

printf("号码:B%03d,",*ticket); if(wait_num==0 || *flag==0) { } else { }

printf("有%d位顾客正在等待。\t",wait_num); print_time();

printf("当前没有顾客正在等待。\t"); print_time();

//wait_num大于等于0或者flag

//获取当前等待的人数

大于0,表示当前无人等待

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

if(get_sem_val(semid, INDEX_SIGNAL_C) > 0) { }

if( wait_sem(semid, INDEX_SIGNAL_SEAT) )

*ticket=get_sem_val(semid, INDEX_SIGNAL_CUSTOMER) + 1;

//

sem_p(semid, INDEX_SIGNAL_SEAT);

//等待操作信号量释放

sem_p(semid, INDEX_MUTEX);

//锁定操作信号量

//如果没有空座,则等

*flag=0;

//将标志为设为0,标示没有顾客在等待,不用排队

//跳转到直接取票

goto goto_C;

//检查,如果有空窗

口,则直接取票,不用判断座位

if( sem_v(semid, INDEX_SIGNAL_CUSTOMER) !=0) { }

sem_v(semid, INDEX_MUTEX); break;

if(get_sem_val(semid, INDEX_SIGNAL_SEAT) == 0) { }

if( (wait_num=my_random()%10)<5)

return 0;

//返回0表示顾客选择离开

printf("目前没有空座位,但该顾客选择了等待。\n\n");

//释放控制进程访问资源的信号量

printf("对不起,V操作失败!"); exit(1); return -1;

//记录目前办理

过业务和等待办理业务的顾客总数

print_time();

case 'C':

待空座

goto_C: if( wait_sem(semid, INDEX_MUTEX) )

分配票号(票号是根据当天的总顾客数排的)

wait_num = (signal_seat - get_sem_val(semid,INDEX_SIGNAL_SEAT) ) -1;

printf("号码:C%03d,",*ticket); if(wait_num==0 || *flag==0) {

printf("当前没有顾客正在等待。\t");

//wait_num大于等于0或者flag

//获取当前等待的人数

大于0,表示当前无人等待

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

}

/*顾客接受服务函数*/

void service(int semid, char identifyLabel, int * ticket, int *flag) {

}

else { }

printf("顾客C%03d正在等待办理业务。\t",*ticket); print_time();

printf("有%d位顾客正在等待。\t",wait_num); print_time();

if( sem_v(semid, INDEX_SIGNAL_CUSTOMER) !=0) { }

sem_v(semid, INDEX_MUTEX); break;

printf("对不起,V操作失败!"); exit(1); return -1;

//记录目前办理

过业务和等待办理业务的顾客总数

//释放控制进程访问资源的信号量

} return 1;

int need_time=0; switch(identifyLabel) {

case 'A':

//用于记录该顾客需要的服务时间

if(wait_sem(semid, INDEX_SIGNAL_A))

sem_p(semid, INDEX_SIGNAL_A); sem_p(semid, INDEX_MUTEX);

if( wait_sem(semid, INDEX_MUTEX) )

//等待有空闲窗口 //走向窗口,即将接受服务 //等待操作信号量释放 //锁定操作信号量

printf("顾客A%03d开始在对私窗口办理业务。\t",*ticket); print_time();

操作系统,利用同步、互斥信号量实现银行的排队叫号系统。 实验环境Linux 本程序是基于进程的,而不是基于线程的。如有疑问,请发送邮件至1993wbb@sina.cn寻求解答。

顾客开始办理业务时即走向了窗口,需要释放座位

sem_v(semid, INDEX_SIGNAL_SEAT);

//释放控制进程访问资源的信号量

sem_v(semid, INDEX_MUTEX);

/*接受服务(服务时长为1分钟以内并且大于10秒的任意时间)*/ need_time=my_random()%50 + 10;

printf("该顾客需要的服务时长为:%d秒。\t", need_time); print_time(); sleep(need_time);

//将该顾客进程挂起need_time秒,模拟该顾客正在接

受服务

if( wait_sem(semid, INDEX_MUTEX) )

sem_p(semid, INDEX_MUTEX);

//等待操作信号量释放 //锁定操作信号量

printf("顾客A%03d业务办理完毕。\t",*ticket); print_time();

sem_v(semid, INDEX_SIGNAL_A); sem_v(semid, INDEX_MUTEX); break;

if(wait_sem(semid, INDEX_SIGNAL_B))

sem_p(semid, INDEX_SIGNAL_B); sem_p(semid, INDEX_MUTEX);

if( wait_sem(semid, INDEX_MUTEX) )

//等待有空闲窗口 //走向窗口,即将接受服务 //等待操作信号量释放 //锁定操作信号量

//业务办理完成,离开窗口 //释放控制进程访问资源的信号量

case 'B':

printf("顾客B%03d开始在对公窗口办理业务。\t",*ticket); print_time(); if(*flag==1)

//如果该顾客在进入银行取票后等待了窗口,那么

顾客开始办理业务时即走向了窗口,需要释放座位

sem_v(semid, INDEX_SIGNAL_SEAT);

//释放控制进程访问资源的信号量

sem_v(semid, INDEX_MUTEX);

/*接受服务(服务时长为1分钟以内并且大于10秒的任意时间)*/ need_time=my_random()%50 + 10;

printf("该顾客需要的服务时长为:%d秒。\t", need_time); print_time(); sleep(need_time);

//将该顾客进程挂起need_time秒,模拟该顾客正在接

受服务

操作系统利用信号量实现银行叫号排队系统 课程设计实验报告.doc 将本文的Word文档下载到电脑

    精彩图片

    热门精选

    大家正在看

    × 游客快捷下载通道(下载后可以自由复制和排版)

    限时特价:7 元/份 原价:20元

    支付方式:

    开通VIP包月会员 特价:29元/月

    注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
    微信:fanwen365 QQ:370150219