logo资料库

操作系统进程间通信实验.pdf

第1页 / 共9页
第2页 / 共9页
第3页 / 共9页
第4页 / 共9页
第5页 / 共9页
第6页 / 共9页
第7页 / 共9页
第8页 / 共9页
资料共9页,剩余部分请下载后查看
操作系统实验 1 银行柜员服务问题 操作系统实验一 银行柜员服务问题 实验报告 邵鑫 2016011111 2018 年 11 月 5 日 一、实验目的 1. 通过对进程间通信同步/互斥问题的编程实现,加深理解信号量和 P、V 操作的原理; 2. 对 Windows 或 Linux 涉及的几种互斥、同步机制有更进一步的了解; 3. 熟悉 Windows 或 Linux 中定义的与互斥、同步有关的函数。 二、实验内容 ·问题描述: 银行有 n 个柜员负责为顾客服务,顾客进入银行先取一个号码,然后等着叫号。当某个 柜员空闲下来,就叫下一个号。 编程实现该问题,用 P、V 操作实现柜员和顾客的同步。 ·实现要求: 1. 某个号码只能由一名顾客取得; 2. 不能有多于一个柜员叫同一个号; 3. 有顾客的时候,柜员才叫号; 4. 无柜员空闲的时候,顾客需要等待; 5. 无顾客的时候,柜员需要等待。 ·实现提示: 1. 互斥对象:顾客拿号,柜员叫号; 2. 同步对象:顾客和柜员; 3. 等待同步对象的队列:等待的顾客,等待的柜员; 4. 所有数据结构在访问时也需要互斥。 第 1 页 共 9 页
操作系统实验 1 银行柜员服务问题 三、设计思路 ①怎样输入输出? 输入输出数据的保存:使用结构体数组,非常方便 ②如何模拟银行排队的过程? 对每一个柜员和顾客创建一个线程来模拟他们的行为。进程间通信使用 WINDOWS 提供的 同步和互斥量 semaphore 和 Mutex:创建同步信号量 sema,确保柜员处于等待状态,创建顾客 互斥量 cusmutex,防止多个顾客同时进入银行的时候拿到一样的号。创建柜员互斥量 servermutex,防止一个柜员服务多个顾客。 ③进程间同步与互斥的操作顺序和逻辑关系? ·顾客进程睡眠相应时长模拟不同时间进入银行的过程 ·顾客进入银行: customermutex:P ·顾客取到号码: sema:V ·柜台开始服务时: servermutex:P sema:P ·柜台结束服务: servermutex:V customermutex:V 四、程序结构与主要代码分析 程序结构基本如下 确定参数:最大服务顾客数和柜台数目 初始化 建立核心变量:待服务顾客数、已服务顾客数、顾客柜员编号 定义输入顾客、输出顾客、互斥/同步量数据类型 定义线程 main 函数 定义柜台服务线程 定义顾客排号线程 读入数据 初始化互斥/同步量 创建线程 运行线程,等待顾客服务完毕 关闭线程 输出结果 关键代码: 创建线程与控制退出: for (int i = 1; i <= customer_number; i++)//创建顾客线程 CUS[i] = CreateThread(NULL, 0, PVcustomer, LPVOID(i), 0, NULL); for (int j = 1; j <= SERVER; j++)//创建柜员线程 SER[j] = CreateThread(NULL, 0, PVserver, LPVOID(j), 0, NULL); 第 2 页 共 9 页
操作系统实验 1 银行柜员服务问题 while (customer_served != customer_number)//直到所有顾客均被服务 进程间同步与互斥: DWORD WINAPI PVcustomer(PVOID PV) { } int num = (int)PV; customer_count++; lineout[customer_count].number = linein[num].number; lineout[customer_count].time_in = linein[num].time_in; lineout[customer_count].time_need = linein[num].time_need; Sleep(1000 * linein[num].time_in);//睡眠,直到进入 WaitForSingleObject(cusmutex, INFINITE);//这是取号 ReleaseMutex(cusmutex);//取完号就可以释放这个互斥量了 ReleaseSemaphore(sema, 1, NULL);//寻求柜员 return 0; DWORD WINAPI PVserver(PVOID PV) { while (true) { WaitForSingleObject(sema, INFINITE);//等待顾客取号 WaitForSingleObject(servermutex, INFINITE);//等待柜员互斥量,占用柜台 ReleaseMutex(servermutex);//我考虑过使用互斥量数组,如果有柜员编号,应该在结束 后释放。如果只使用一个互斥量,在这里释放 server_number++; int n = server_number; time_t starttime = time(NULL); Sleep(1000 * lineout[n].time_need); time_t endtime = time(NULL); lineout[n].time_begin = starttime - time_begin; lineout[n].time_end = endtime - time_begin; lineout[n].servernumber = (int)PV; customer_served++;//服务完的顾客数目加一 } return 0; } 五、运行情况与结果分析 在运行通过了样例数据之后,为了体现互斥与同步的正确性,我构造了如下数据,其中 既有同时进入银行的顾客(考验顾客互斥量),也有同时开始服务的顾客(考验柜员互斥量) 测试数据如下表所示: 顾客编号 进入时间 服务时长 1 2 3 4 1 5 6 6 第 3 页 共 9 页 10 2 3 2
操作系统实验 1 银行柜员服务问题 6 7 7 8 9 10 3 10 3 2 1 1 5 6 7 8 9 10 首先令柜员数目为 3,运行结果如下: 表- 测试数据 图- 运行结果 1 很明显该结果是正确的,同时我们也看到,由于柜员数目较少,许多顾客等了一段时间, 9 号顾客整整等了 5 分钟。 修改柜员数目再次运行,这次令柜员数目为 5,运行结果如下图所示: 第 4 页 共 9 页
操作系统实验 1 银行柜员服务问题 可以看到,所有柜员都参与到工作中,同时非常幸运的是,没有顾客等待,他们都是拿 到号之后立刻找到了对应了柜员进行服务。 图- 运行结果 2 六、思考题解答 1. 柜员人数和顾客人数对结果分别有什么影响? 解: 在上面的运行结果中我们也分析了。 ①柜员人数的影响: 主要影响的是总时长(即银行柜员什么时候可以下班,也就是最后一位顾客什么时候走) 和平均等待时间。其中平均等待时间这样计算: number ( t customer _ begin t average _ waiting  i 1    t in ) customer _ number 以我们在运行结果分析中使用的输入数据为例,改变柜员数目,得到如下结果: 平均等待时间/min 总时间/min 柜员人数 2 3 4 5 6 22 19 18 17 17 表- 思考题分析 5.3 2.6 0.9 0.2 0 可以明显看到,在顾客进入银行的时间和服务时长分布相同的情况下,柜员越多,做完 所有服务所需总时间长度越少,同时,顾客的平均等待时间越短。当柜员数目足够大时,再 增加柜员数目将不会显著减少总时长和平均等待时间。 ②顾客人数的影响:如果我们固定柜员的人数,当顾客数目增大时,显然总时长会增加。 第 5 页 共 9 页
操作系统实验 1 银行柜员服务问题 如果单位时间内进入银行的顾客数目增大,那么顾客的平均等待时间会增大,这些都是显而 易见的。调试时对不同数据规模的测试数据的运行也说明了这一点。 2. 实现互斥的方法有哪些?各自有什么特点?效率如何? 优点 简单 方法 禁止中断 锁变量 严格轮转法 缺点 把禁止中断的权利交给用户进程,导 致系统可靠性较差;不适用于多处理 器 可能使两个进程同时处于临界区;忙 等待 有可能使临界区外的进程阻塞其他 进程;忙等待 PETERSON 解决了互斥访问问题,克服了强制 算法 轮转法缺点,可以正常工作 忙等待 硬件指令 方法 信号量 管程 适用于任意数目的进程;简单,易 验证其正确性;支持进程中存在多 个临界区 适用于任意数目的进程;解决忙等 待问题 提高代码可读性,便于修改和维护, 正确性易于保证 忙等待,耗费CPU时间 实现较复杂,需要系统调用;同步操 作分散,易读性较差,不利于修改和 维护,正确性难以保证 编译器必须识别管程并用某种方式 对其互斥作出安排;多数语言不支 持,不广泛 消息传递 适用于分布式系统 更加复杂 七、实验总结 表- 不同互斥方法的比较 本次实验的难点主要在于理解 WINDOWS 编程中的同步信号量和互斥信号量。由于有大量 陌生的函数和对象,使得我编程和调试的过程特别艰难。不过好在问题本身很有意思,这让 整个编程的过程充满了乐趣。通过这次实验,我学会了 WINDOW 中线程的基本使用方法,了 解了同步和互斥在 WINDOW 下如何实现,更加深入地理解了简单的进程间通信问题。 实验中还碰到了一些细碎的问题, ①我发现在不同的教程中,WINAPI 后面的参数既有 PVOID 也有 LPVOID,查阅资料之后 发现是 16 位 CPU 的历史遗留问题,LPVIOD 为长寻址,现在使用起来已经没有什么区别了。 ②其实在创建线程的过程中,使用 CreatThread 并不是一种很好的方法,_beginthreade -x 可能是一种更好的方法。不过这里不是实际项目,就使用 CreatThread 方便分析。 ③WaitForMultipleObjects 真的是一个功能非常强大的函数,它可以在很多进程的应 第 6 页 共 9 页
操作系统实验 1 银行柜员服务问题 用下表示等待某个信号量。我在查询相关知识学习后,学会了最简单的应用。 调试过程出了以下问题,在我分析之后一一解决: ①由于用到了很多之前没有用过的函数和对象,我在设置参数的经常出毛病。比如对同 步信号量参数不熟悉,一开始把两个参数(0 和 MAX_N 定义反了) ②犯了两个非常愚蠢的错误,一是数组下标从 0 开始却到小于等于总数,导致输入实际 上多了一个;而是定义了宏 SEVER 之后,直接开了一个 SEVER 大小的静态数组,然后使用了 SER[SERVER],导致数组越界,使得计数变量从一个非常奇怪的数字开始(应该是内存被占 用了) ③一开始我定义了一个柜员互斥量数组,使用方法是:等待->使用->释放。后来想想觉 得没有必要就改成了使用一个柜员互斥量,但是忘记了这时候需要改变顺序,应该是等待-> 释放->使用,结果运行结果发生了错误(即所有人都跑到一个柜员那去了),这让我苦恼了 很久,后来在线程中逐步输出当前柜员编号和服务顾客编号,我才发现问题在于释放的顺序 不正确。 附录 源代码 //Copyright by Shaoxin, Department of Electronic Engineering of Tsinghua University, China #define SERVER 3//定义柜员数目 #define MAX_N 50//定义最大顾客数目,考虑到一般银行的规模,这里定义为 50 #include #include #include #include #include #include using namespace std; HANDLE CUS[MAX_N+4], SER[SERVER+4];//注意扩大数组容量,防止溢出 int customer_served = 0;//已经服务过的顾客 int customer_number = 0;//顾客总数 int customer_count = 0;//顾客序号 int server_number = 0;//柜员序号 HANDLE sema = CreateSemaphore(NULL, 0, MAX_N, NULL);//同步信号量,确保柜员处于等待状态 HANDLE cusmutex = CreateMutex(NULL, FALSE, NULL);//顾客互斥量,防止多个顾客同时进入银行 的时候拿到一样的号 HANDLE servermutex = CreateMutex(NULL, FALSE, NULL);//柜员互斥量,防止一个柜员服务多个顾 客 struct customer//顾客信息结构体 { }; int number; int time_in; int time_need; struct printinfo//输出信息结构体 { int number; 第 7 页 共 9 页
操作系统实验 1 银行柜员服务问题 int time_in; int time_begin; int time_end; int servernumber; int time_need; }; time_t time_begin = time(NULL);//开始计时 customer linein[MAX_N];//储存输入顾客信息 printinfo lineout[MAX_N];//储存输出顾客信息 DWORD WINAPI PVcustomer(PVOID PV) { } int num = (int)PV; customer_count++; lineout[customer_count].number = linein[num].number; lineout[customer_count].time_in = linein[num].time_in; lineout[customer_count].time_need = linein[num].time_need; Sleep(1000 * linein[num].time_in);//睡眠,直到进入 WaitForSingleObject(cusmutex, INFINITE);//这是取号 ReleaseMutex(cusmutex);//取完号就可以释放这个互斥量了 ReleaseSemaphore(sema, 1, NULL);//寻求柜员 return 0; DWORD WINAPI PVserver(PVOID PV) { while (true) { WaitForSingleObject(sema, INFINITE);//等待顾客取号 WaitForSingleObject(servermutex, INFINITE);//等待柜员互斥量,占用柜台 ReleaseMutex(servermutex);//我考虑过使用互斥量数组,如果有柜员编号,应该在结束 后释放。如果只使用一个互斥量,在这里释放 server_number++; int n = server_number; time_t starttime = time(NULL); Sleep(1000 * lineout[n].time_need); time_t endtime = time(NULL); lineout[n].time_begin = starttime - time_begin; lineout[n].time_end = endtime - time_begin; lineout[n].servernumber = (int)PV; customer_served++;//服务完的顾客数目加一 } return 0; } int main() { 第 8 页 共 9 页
分享到:
收藏