2022年操作系统实验四进程的同步 .pdf
操作系统实验报告课程名称操作系统实验课程编号0920311 实验项目名称操作系统的启动学号年级2011级姓名专业软件工程学生所在学院软件学院指导教师王红滨实验室名称地点软件工程实验室21B282 哈尔滨工程大学软件学院名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 1 页,共 24 页 - - - - - - - - - 1 第六讲进程的同步一、实验概述1. 实验名称实验系统的启动2. 实验目的1).使用 EOS 的信号量编程解决生产者消费者问题,理解进程同步的意义。2).调试跟踪EOS 的信号量的工作过程,理解进程同步的原理。3).修改 EOS 的信号量算法,使之支持等待超时唤醒功能(有限等待),加深理解进程同步的原理。3. 实验类型(验证、设计)验证4. 实验内容1).准备实验2).使用 EOS 的信号量解决生产者消费者问题3).调试 EOS 信号量的工作过程创建信号量等待释放信号量等待信号量(不阻塞)释放信号量(不唤醒)等待信号量(阻塞)释放信号量(唤醒)4).修改 EOS 的信号量算法在目前 EOS Kernel 项目的 ps/semaphore.c文件中, PsWaitForSemaphore函 数 的Milliseconds 参 数 只 能 是INFINITE , PsReleaseSemaphore 函 数 的ReleaseCount 参数只能是1。现在要求同时修改PsWaitForSemaphore函数和PsReleaseSemaphore 函数中的代码,使这两个参数能够真正起到作用,使信号量对象支持等待超时唤醒功能和批量释放功能。二、实验环境操作系统: windows xp 编译环境: OS Lab 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 2 页,共 24 页 - - - - - - - - - 2 三、实验过程1. 设计思路和流程图图 3-1.整体试验流程图准备实验使用 EOS 的信号量解决生产者消费者问题调试EOS 信号量的工作过程创建信号量释 放 信号量(唤醒)等 待 信号量(阻塞)释 放 信号量(不唤醒)等 待 信号量(不阻塞)等 待 释放 信 号量修改 EOS 的信号量算法结束实验名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 3 页,共 24 页 - - - - - - - - - 3 图 3-2.Main 函数流程图、生产者消费、消费者流程图main 函数开始创建 Mutex 对象创建 Empty 信号量对象创建 Full 信号量对象创建生产者线程创建消费者线程等待生产者线程和消费者线程结束关闭句柄main 函数结束Producer函数开始生产完毕?等待 Empty 信号量等待 Mutex 对象生产一个产品,占用一个缓冲区循环向后移动缓冲区指针释放 Mutex 对象释放 Full 信号量对等待 500毫秒Producer函 数结束Consumer 函数开始消 费完 毕等待 Full 信号量对象等待 Mutex 对象消费一个产品,清空一个缓冲区循环向后移动缓释放 Mutex 对象释放 Empty 信号量对象前 10个 产等待 2000 毫秒等待100毫秒Consumer函数结束名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 4 页,共 24 页 - - - - - - - - - 4 2. 需要解决的问题及解答(1). 思 考 在ps/semaphore.c 文 件 内 的PsWaitForSemaphore 和PsReleaseSemaphore 函数中,为什么要使用原子操作?答: 在执行等待信号量和释放信号量的时候,是不允许 cpu响应外部中断的,如果此时 cpu响应了外部中断,会产生不可预料的结果, 无法正常完成原子操作。(2). 绘制 ps/semaphore.c文件内 PsWaitForSemaphore 和 PsReleaseSemaphore函数的流程图。1).PsWaitForSemaphore 函数流程图。2). PsReleaseSemaphore 函数流程图开始1.原子操作前关中断3.If Wait 操作的信号量大于0 4.If Wait 操作的信号量大于0 执行 P操作,否则返回执行3 5.P 操作结束2.开始原子操作P 操作结束开始1.原子操作前关中断名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 5 页,共 24 页 - - - - - - - - - 5 (3). P143生产者在生产了 13 号产品后本来要继续生产14 号产品,可此时生产者为什么必须等待消费者消费了4 号产品后,才能生产14 号产品呢?生产者和消费者是怎样使用同步对象来实现该同步过程的呢?答:这是因为临界资源的限制。缓冲区只有十个空间,现在消费比生产慢,此时分别装了 4 到 13 号产品,而消费者只消费了3 个。此时装入产品与产品的存储空间成为直接相互制约关系, 再消费者消费了下一个产品之前生产者不能继续装入。因此,可解释生产者为什么必须等待消费者消费了4 号产品后才能生产14 号产品。(4). 根据本实验 3.3.2 节中设置断点和调试的方法,自己设计一个类似的调试方案来验证消费者线程在消费24 号产品时会被阻塞,直到生产者线程生产了24 号产品后,消费者线程才被唤醒并继续执行的过程。2.开始原子操作V 操作3.if P 和 V 操作的信号量之和大于缓冲队列长度4.如果 3 成立返回“信号数目量超出范围”函数结束,否则记录当前信号量的值5.执行一次信号量的释放操作。使信号量的值增加 1。 如果 P 操作控制的信号量不大于零唤醒等待进程,返回,唤醒成功,函数结束。6.if 被阻塞进程的数量小于要释放的信号量的个数,唤醒等待队列中的进程,直到等待队列为空。返回,唤醒成功,函数结束。7.if 被阻塞的线程数量大于等于ReleaseCount,则循环结束后,有ReleaseCount 个线程会被唤醒,而且信号量计数的值仍然为0;如果被阻塞的线程数量小于ReleaseCount,则循环结束后,所有被阻塞的线程都会被唤醒,并且信号量的计数值=ReleaseCount之前被阻塞线程的数量+之前信号量的计数值。函数结束结束名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 6 页,共 24 页 - - - - - - - - - 6 答:可以按照下面的步骤进行调试(1) 删除所有的断点。(2) 按 F5启动调试。 OS Lab会首先弹出一个调试异常对话框。(3) 在调试异常对话框中选择 “ 是” ,调试会中断。(4) 在Consumer 函 数 中 等 待Full信 号 量 的 代 码 行 ( 第 173 行 )WaitForSingleObject(FullSemaphoreHandle, INFINITE); 添加一个断点。(5) 在“ 断点” 窗口(按 Alt+F9 打开)中此断点的名称上点击右键。(6) 在弹出的快捷菜单中选择 “ 条件” 。(7) 在“ 断点条件 ” 对话框(按 F1 获得帮助) 的表达式编辑框中, 输入表达式“i = 24”。(8) 点击“ 断点条件 ” 对话框中的 “ 确定” 按钮。(9) 按 F5继续调试。只有当消费者线程尝试消费24 号产品时才会在该条件断点处中断。3. 主要数据结构、实现代码及其说明1).对 PsWaitForSemaphore 函数的修改:PsWaitForSemaphore( IN PSEMAPHORE Semaphore, IN ULONG Milliseconds ) /*+ 功能描述:信号量的Wait 操作( P 操作) 。参数:Semaphore - Wait 操作的信号量对象。Milliseconds - 等待超时上限,单位毫秒。返回值:STATUS_SUCCESS 。当你修改信号量使之支持超时唤醒功能后,如果等待超时,应该返回名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 7 页,共 24 页 - - - - - - - - - 7 STATUS_TIMEOUT 。-*/ BOOL IntState; STATUS a; ASSERT(KeGetIntNesting() = 0); / 中断环境下不能调用此函数。IntState = KeEnableInterrupts(FALSE); / 开始原子操作,禁止中断。/ / 目前仅实现了标准记录型信号量, 不支持超时唤醒功能, 所以 PspWait 函数/ 的第二个参数的值只能是INFINITE 。/ if (Semaphore-Count0) Semaphore-Count-; a=STATUS_SUCCESS; else if(Semaphore-Count=0) a=PspWait(&Semaphore-WaitListHead, Milliseconds); KeEnableInterrupts(IntState); / 原子操作完成,恢复中断。return a; 对PsReleaseSemaphore函数的修改:PsReleaseSemaphore( IN PSEMAPHORE Semaphore, IN LONG ReleaseCount, OUT PLONG PreviousCount ) /*+ 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 8 页,共 24 页 - - - - - - - - - 8 功能描述:信号量的 Signal 操作( V 操作) 。参数:Semaphore - Wait 操作的信号量对象。ReleaseCount - 信号量计数增加的数量。当前只能为1。当你修改信号量使之支持超时唤醒功能后,此参数的值能够大于等于1。PreviousCount - 返回信号量计数在增加之前的值。返回值:如果成功释放信号量,返回STATUS_SUCCESS 。-*/ STATUS Status; BOOL IntState; / int bobo; IntState = KeEnableInterrupts(FALSE); / 开始原子操作,禁止中断。if (Semaphore-Count + ReleaseCount Semaphore-MaximumCount) Status = STATUS_SEMAPHORE_LIMIT_EXCEEDED; else / / 记录当前的信号量的值。/ if (NULL != PreviousCount) *PreviousCount = Semaphore-Count; / / 目前仅实现了标准记录型信号量,每执行一次信号量的释放操作/ 只能使信号量的值增加1。/ if(ReleaseCount0&!(ListIsEmpty(&Semaphore-WaitListHead);ReleaseCount-名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 9 页,共 24 页 - - - - - - - - - 9 =1) PspWakeThread(&Semaphore-WaitListHead,STATUS_SUCCESS); Semaphore-Count+=ReleaseCount; Status = STATUS_SUCCESS; KeEnableInterrupts(IntState); / 原子操作完成,恢复中断。return Status; 4. 源程序并附上注释#include psp.h VOID PsInitializeSemaphore( IN PSEMAPHORE Semaphore, IN LONG InitialCount, IN LONG MaximumCount ) /*+ 功能描述:初始化信号量结构体。参数:Semaphore - 要初始化的信号量结构体指针。InitialCount - 信号量的初始值,不能小于0 且不能大于MaximumCount。MaximumCount - 信号量的最大值,必须大于0。返回值:无。-*/ ASSERT(InitialCount = 0 & InitialCount 0); Semaphore-Count = InitialCount; Semaphore-MaximumCount = MaximumCount; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 10 页,共 24 页 - - - - - - - - - 10 ListInitializeHead(&Semaphore-WaitListHead); STATUS PsWaitForSemaphore( IN PSEMAPHORE Semaphore, IN ULONG Milliseconds ) /*+ 功能描述:信号量的 Wait 操作(P 操作) 。参数:Semaphore - Wait 操作的信号量对象。Milliseconds - 等待超时上限,单位毫秒。返回值:STATUS_SUCCESS 。当你修改信号量使之支持超时唤醒功能后,如果等待超时,应该返回STATUS_TIMEOUT 。-*/ BOOL IntState; STATUS a; ASSERT(KeGetIntNesting() = 0); / 中断环境下不能调用此函数。IntState = KeEnableInterrupts(FALSE); / 开始原子操作,禁止中断。/ / 目前仅实现了标准记录型信号量,不支持超时唤醒功能, 所以 PspWait 函数/ 的第二个参数的值只能是INFINITE 。/ if (Semaphore-Count0) 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 11 页,共 24 页 - - - - - - - - - 11 Semaphore-Count-; a=STATUS_SUCCESS; else if(Semaphore-Count=0) a=PspWait(&Semaphore-WaitListHead, Milliseconds); KeEnableInterrupts(IntState); / 原子操作完成,恢复中断。return a; STATUS PsReleaseSemaphore( IN PSEMAPHORE Semaphore, IN LONG ReleaseCount, OUT PLONG PreviousCount ) /*+ 功能描述:信号量的 Signal 操作( V 操作) 。参数:Semaphore - Wait 操作的信号量对象。ReleaseCount - 信号量计数增加的数量。当前只能为1。当你修改信号量使之支持超时唤醒功能后,此参数的值能够大于等于1。PreviousCount - 返回信号量计数在增加之前的值。返回值:如果成功释放信号量,返回STATUS_SUCCESS 。-*/ 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 12 页,共 24 页 - - - - - - - - - 12 STATUS Status; BOOL IntState; IntState = KeEnableInterrupts(FALSE); / 开始原子操作,禁止中断。if (Semaphore-Count + ReleaseCount Semaphore-MaximumCount) Status = STATUS_SEMAPHORE_LIMIT_EXCEEDED; else / / 记录当前的信号量的值。/ if (NULL != PreviousCount) *PreviousCount = Semaphore-Count; / / 目前仅实现了标准记录型信号量,每执行一次信号量的释放操作/ 只能使信号量的值增加1。/ if(ReleaseCount0&!(ListIsEmpty(&Semaphore-WaitListHead);ReleaseCount-=1) PspWakeThread(&Semaphore-WaitListHead,STATUS_SUCCESS); Semaphore-Count+=ReleaseCount; Status = STATUS_SUCCESS; KeEnableInterrupts(IntState); / 原子操作完成,恢复中断。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 13 页,共 24 页 - - - - - - - - - 13 return Status; / / / 下面是和信号量对象类型相关的代码。/ / / 信号量对象类型指针。/ POBJECT_TYPE PspSemaphoreType = NULL; / / 用于初始化semaphore 结构体的参数结构体。/ typedef struct _SEM_CREATE_PARAM LONG InitialCount; LONG MaximumCount; SEM_CREATE_PARAM, *PSEM_CREATE_PARAM; / / semaphore 对象的构造函数,在创建新semaphore 对象时被调用。/ VOID PspOnCreateSemaphoreObject( IN PVOID SemaphoreObject, IN ULONG_PTR CreateParam ) PsInitializeSemaphore( (PSEMAPHORE)SemaphoreObject, 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 14 页,共 24 页 - - - - - - - - - 14 (PSEM_CREATE_PARAM)CreateParam)-InitialCount, (PSEM_CREATE_PARAM)CreateParam)-MaximumCount ); / / semaphore 对象类型的初始化函数。/ VOID PspCreateSemaphoreObjectType( VOID ) STATUS Status; OBJECT_TYPE_INITIALIZER Initializer; Initializer.Create = PspOnCreateSemaphoreObject; Initializer.Delete = NULL; Initializer.Wait = (OB_WAIT_METHOD)PsWaitForSemaphore; Initializer.Read = NULL; Initializer.Write = NULL; Status = ObCreateObjectType(SEMAPHORE, &Initializer, &PspSemaphoreType); if (!EOS_SUCCESS(Status) KeBugCheck(Failed to create semaphore object type!); 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 15 页,共 24 页 - - - - - - - - - 15 / / semaphore 对象的构造函数。/ STATUS PsCreateSemaphoreObject( IN LONG InitialCount, IN LONG MaximumCount, IN PSTR Name, OUT PHANDLE SemaphoreHandle ) STATUS Status; PVOID SemaphoreObject; SEM_CREATE_PARAM CreateParam; if(InitialCount 0 | MaximumCount MaximumCount) return STATUS_INVALID_PARAMETER; / / 创建信号量对象。/ CreateParam.InitialCount = InitialCount; CreateParam.MaximumCount = MaximumCount; Status = ObCreateObject( PspSemaphoreType, Name, sizeof(SEMAPHORE), (ULONG_PTR)&CreateParam, &SemaphoreObject); if (!EOS_SUCCESS(Status) return Status; 名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 16 页,共 24 页 - - - - - - - - - 16 Status = ObCreateHandle(SemaphoreObject, SemaphoreHandle); if (!EOS_SUCCESS(Status) ObDerefObject(SemaphoreObject); return Status; / / semaphore 对象的 signal 操作函数。/ STATUS PsReleaseSemaphoreObject( IN HANDLE Handle, IN LONG ReleaseCount, IN PLONG PreviousCount ) STATUS Status; PSEMAPHORE Semaphore; if (ReleaseCount WaitListHead, INFINITE); 代码行(第78 行)添加一个断点。6).按 F5 继续调试,并立即激活虚拟机窗口查看输出。开始时生产者、消费者都不会被信号量阻塞,同步执行一段时间后才在断点处中断。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 19 页,共 24 页 - - - - - - - - - 19 7).中断后,查看“调用堆栈”窗口,有Producer 函数对应的堆栈帧,说明此次调用是从生产者线程函数进入的。8).在“调用堆栈”窗口中双击Producer 函数所在的堆栈帧,绿色箭头指向等待Empty 信号量的代码行, 查看 Producer 函数中变量i 的值为 14,表示生产者线程正在尝试生产14 号产品。9).在“调用堆栈”窗口中双击PsWaitForSemaphore 函数的堆栈帧,查看Empty 信号量计数( Semaphore-Count)的值为 -1,所以会调用PspWait 函数将生产者线程放入Empty 信号量的等待队列中进行等待(让出CPU) 。10).激活虚拟机窗口查看输出的结果。生产了从0 到 13 的 14 个产品,但是只消费了从0 到3 的 4 个产品, 所以缓冲池中的10 个缓冲区就都被占用了,这与之前调试的结果是一致的。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 20 页,共 24 页 - - - - - - - - - 20 d 释放信号量(唤醒)1).删除所有断点。2). 在 eosapp.c 文件的Consumer 函数中,释放Empty 信号量的代码行(第180 行)ReleaseSemaphore(EmptySemaphoreHandle, 1, NULL); 添加一个断点。3).按 F5 继续调试,到断点处中断。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 21 页,共 24 页 - - - - - - - - - 21 4).查看 Consumer 函数中变量i 的值为 4,说明已经消费了4 号产品。5).进入 PsReleaseSemaphore函数。6).查看PsReleaseSemaphore 函数中Empty 信号量计数 (Semaphore-Count)的值为 -1,和生产者线程被阻塞时的值是一致的。7). 按F10单 步 调 试PsReleaseSemaphore 函 数 , 直 到 在 代 码 行 ( 第132 行 )PspWakeThread(&Semaphore-WaitListHead, STA TUS_SUCCESS); 处中断。 此时 Empty 信号量计数的值已经由-1 增加为了0,需要调用PspWakeThread 函数唤醒阻塞在Empty 信号量等待队列中的生产者线程(放入就绪队列中),然后调用PspSchedule 函数执行调度,这样生产者线程就得以继续执行。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 22 页,共 24 页 - - - - - - - - - 22 E.测试修改后的EOS 信号量算法1).使用修改完毕的EOS Kernel 项目生成完全版本的SDK 文件夹,并覆盖之前的生产者消费用程序项目的SDK 文件夹。2).按 F5 调试执行原有的生产者消费者应用程序项目,结果必须仍然与图13-2 一致。如果有错可以调试内核代码来查找错误,然后在内核项目中修改,并重复步骤1。3).将Producer函数中等待Empty信号量的代码行WaitForSingleObject(EmptySemaphoreHandle, INFINITE); 替 换 为while(WAIT_TIMEOUT = WaitForSingleObject(EmptySemaphoreHandle, 300) printf(Producer wait for empty semaphore timeoutn); 4).将 Consumer 函数中等待Full 信号量的代码行WaitForSingleObject(FullSemaphoreHandle, INFINITE); 替换为while(WAIT_TIMEOUT = WaitForSingleObject(FullSemaphoreHandle, 300) printf(Consumer wait for full semaphore timeoutn); 5).启动调试新的生产者消费者项目,查看在虚拟机中输出的结果,验证信号量超时等待功能是否能够正常执行。如果有错误, 可以调试内核代码来查找错误,然后在内核项目中修改,并重复步骤1。6).如果超时等待功能已经能够正常执行,可以考虑将消费者线程修改为一次消费两个产品,来测试 ReleaseCount 参数是否能够正常使用。使用实验文件夹中NewConsumer.c 文件中的Consumer 函数替换原有的Consumer 函数。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 23 页,共 24 页 - - - - - - - - - 23 四、实验体会通过本次试验,我对计算机存储结构有了更深的认识,了解了EOS 启动后的状态和行为,理解了操作系统启动后的工作方式。因为编程能力薄弱,第二次课才将信号量算法修改成功,颇费了一番周折,以后要加强编程方面的训练。名师资料总结 - - -精品资料欢迎下载 - - - - - - - - - - - - - - - - - - 名师精心整理 - - - - - - - 第 24 页,共 24 页 - - - - - - - - -