《操作系统实验进程的同步heu.docx》由会员分享,可在线阅读,更多相关《操作系统实验进程的同步heu.docx(19页珍藏版)》请在三一办公上搜索。
1、操作系统实验进程的同步heu操作系统 实 验 报 告 课程名称 实验项目名称 学号 姓名 学生所在学院 实验室名称地点 操作系统实验 进程的同步 课程编号 0906553 年级 专业 指导教师 哈尔滨工程大学 计算机科学与技术学院 第六讲 进程的同步 一、实验概述 1. 实验名称 进程的同步 2. 实验目的 使用 EOS 的信号量编程解决生产者消费者问题,理解进程同步的意义。 调试跟踪 EOS 的信号量的工作过程,理解进程同步的原理。 修改 EOS 的信号量算法,使之支持等待超时唤醒功能,加深理解进程同步的原理。 3. 实验类型 验证型实验,设计性实验 4. 实验内容 准备实验 使用 EOS
2、的信号量解决生产者消费者问题 调试 EOS 信号量的工作过程 1)创建信号量 2)等待释放信号量 3)等待信号量 4)释放信号量 5) 等待信号量 6) 释放信号量 修改 EOS 的信号量算法 二、实验环境 操作系统集成实验环境OS Lab 三、实验过程 1. 设计思路和流程图 2. 算法实现 3. 需要解决的问题及解答 (1). P143生产者在生产了13号产品后本来要继续生产14号产品,可此时生产者为什么必须等待消费者消费了4号产品后,才能生产14号产品呢?生产者和消费者是怎样使用同步对象来实现该同步过程的呢? 答:此时生产了0-13号14个产品,消费了0-3号4个产品,缓冲区都占满了。只
3、有缓冲区有空闲生产者才能生产东西,有权向里面放东西。所以它必须等到消费者,取走产品,有空闲缓冲区时,才继续生产14号产品。 (2). P145-3.4 修改EOS的信号量算法(只看一次消费1个产品的,一次消费2个产品的可以写到实验报告中) 答:见三,四部分 (3). 思考在ps/semaphore.c文件内的PsWaitForSemaphore和PsReleaseSemaphore函数中,为什么要使用原子操作? 答:原子操作要求一旦开始就要运行到结束,不能有中断。在执行等待信号量和释放信号量的时候,不允许cpu响应外部中断,所以使用原子操作。 (4). 绘制ps/semaphore.c文件内P
4、sWaitForSemaphore和PsReleaseSemaphore函数的流程图。 PsWaitForSemaphore N 开始原子操作P操作 原子操作前关中断 Wait操作的信号量大于0 Y 执行P操作 P操作结束 Ps Release Semaphore P和V操作的信号量之N 开始原子操作V操作 原子操作前关中断 和大于缓冲队列长度 Y N P操作控制的信号量不大于0 Y 被阻塞进程量小于要释唤醒等待进程 放的信号量 N 返回“信号数目量超出范围” 释放信号量 Y 记录当前信号量的值 信号量值+1 唤醒队列中进程 结束 返回“唤醒成功” 等待队列为空 N Y 4. 主要数据结构、实
5、现代码及其说明 1)修改PsWaitForSemaphore函数 if (Semaphore-Count0) Semaphore-Count-; flag=STATUS_SUCCESS; /如果信号量大于零,说明尚有资源,可以为线程分配 else flag=PspWait(&Semaphore-WaitListHead, Milliseconds); KeEnableInterrupts(IntState); / 原子操作完成,恢复中断。 return flag; /否则,说明资源数量不够,不能再为线程分配资源,因此要使线程等待 2)修改PsReleaseSemaphore函数 if (Sem
6、aphore-Count + ReleaseCount Semaphore-MaximumCount) Status = STATUS_SEMAPHORE_LIMIT_EXCEEDED; else / / 记录当前的信号量的值。 / if (NULL != PreviousCount) int mm=Semaphore-Count; / / 目前仅实现了标准记录型信号量,每执行一次信号量的释放操作 / 只能使信号量的值增加 1。 / while (!ListIsEmpty(&Semaphore-WaitListHead)&(ReleaseCount) Semaphore-Count=mm+Re
7、leaseCount; / / 可能有线程被唤醒,执行线程调度。 / Status = STATUS_SUCCESS; PspWakeThread(&Semaphore-WaitListHead, STATUS_SUCCESS); PspThreadSchedule; ReleaseCount-; *PreviousCount = Semaphore-Count; 5. 源程序并附上注释 #include psp.h VOID PsInitializeSemaphore( IN PSEMAPHORE Semaphore, IN LONG InitialCount, IN LONG Maximu
8、mCount ) /*+ 功能描述: 初始化信号量结构体。 参数: Semaphore - 要初始化的信号量结构体指针。 InitialCount - 信号量的初始值,不能小于 0 且不能大于 MaximumCount。 MaximumCount - 信号量的最大值,必须大于 0。 返回值: 无。 -*/ ASSERT(InitialCount = 0 & InitialCount 0); Semaphore-Count = InitialCount; Semaphore-MaximumCount = MaximumCount; ListInitializeHead(&Semaphore-Wa
9、itListHead); STATUS PsWaitForSemaphore( IN PSEMAPHORE Semaphore, IN INT Milliseconds, IN STATUS i ) /*+ 功能描述: 信号量的 Wait 操作。 参数: Semaphore - Wait 操作的信号量对象。 Milliseconds - 等待超时上限,单位毫秒。返回值: STATUS_SUCCESS。 当你修改信号量使之支持超时唤醒功能后,如果等待超时,应该返回 STATUS_TIMEOUT。 -*/ BOOL IntState; ASSERT(KeGetIntNesting = 0); /
10、中断环境下不能调用此函数。 IntState = KeEnableInterrupts(FALSE); / 开始原子操作,禁止中断。 / / 目前仅实现了标准记录型信号量,不支持超时唤醒功能,所以 PspWait 函数 / 的第二个参数的值只能是 INFINITE。 / if(Semaphore-Count 0) Semaphore-Count-; i=STATUS_SUCCESS; else i=PspWait(&Semaphore-WaitListHead,Milliseconds ); KeEnableInterrupts(IntState); / 原子操作完成,恢复中断。 return
11、 i; STATUS PsReleaseSemaphore( IN PSEMAPHORE Semaphore, IN LONG ReleaseCount, OUT PLONG PreviousCount ) /*+ 功能描述: 信号量的 Signal 操作。 参数: Semaphore - Wait 操作的信号量对象。 ReleaseCount - 信号量计数增加的数量。当前只能为 1。当你修改信号量使之支持 超时唤醒功能后,此参数的值能够大于等于 1。 PreviousCount - 返回信号量计数在增加之前的值。 返回值: 如果成功释放信号量,返回 STATUS_SUCCESS。 -*/
12、STATUS Status; BOOL IntState; IntState = KeEnableInterrupts(FALSE); / 开始原子操作,禁止中断。 if (Semaphore-Count + ReleaseCount Semaphore-MaximumCount) else / / 记录当前的信号量的值。 / if (NULL != PreviousCount) *PreviousCount = Semaphore-Count; Status = STATUS_SEMAPHORE_LIMIT_EXCEEDED; INT j=Semaphore-Count; KeEnableI
13、nterrupts(IntState); / 原子操作完成,恢复中断。 return Status; POBJECT_TYPE PspSemaphoreType = NULL; / / 用于初始化 semaphore 结构体的参数结构体。 / typedef struct _SEM_CREATE_PARAM LONG InitialCount; LONG MaximumCount; SEM_CREATE_PARAM, *PSEM_CREATE_PARAM; / / semaphore 对象的构造函数,在创建新 semaphore 对象时被调用。 Status = STATUS_SUCCESS;
14、 / / 目前仅实现了标准记录型信号量,每执行一次信号量的释放操作 / 只能使信号量的值增加 1。 / while(!ListIsEmpty(&Semaphore-WaitListHead)&(ReleaseCount) PspWakeThread(&Semaphore-WaitListHead, STATUS_SUCCESS); PspThreadSchedule; ReleaseCount-; Semaphore-Count=j+ReleaseCount; / VOID PspOnCreateSemaphoreObject( IN PVOID SemaphoreObject, IN ULO
15、NG_PTR CreateParam ) PsInitializeSemaphore( (PSEMAPHORE)SemaphoreObject, / / semaphore 对象类型的初始化函数。 / VOID PspCreateSemaphoreObjectType( VOID ) STATUS Status; OBJECT_TYPE_INITIALIZER Initializer; Initializer.Create = PspOnCreateSemaphoreObject; Initializer.Delete = NULL; Initializer.Wait = (OB_WAIT_M
16、ETHOD)PsWaitForSemaphore; Initializer.Read = NULL; Initializer.Write = NULL; Status = ObCreateObjectType(SEMAPHORE, &Initializer, &PspSemaphoreType); (PSEM_CREATE_PARAM)CreateParam)-InitialCount, (PSEM_CREATE_PARAM)CreateParam)-MaximumCount ); if (!EOS_SUCCESS(Status) / / semaphore 对象的构造函数。 / STATUS
17、 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) / / 创建信号量对象。 / CreateParam.InitialCount = InitialCount; CreateParam.Max
18、imumCount = MaximumCount; Status = ObCreateObject( PspSemaphoreType, return STATUS_INVALID_PARAMETER; KeBugCheck(Failed to create semaphore object type!); Name, sizeof(SEMAPHORE), (ULONG_PTR)&CreateParam, &SemaphoreObject); if (!EOS_SUCCESS(Status) Status = ObCreateHandle(SemaphoreObject, SemaphoreH
19、andle); if (!EOS_SUCCESS(Status) return Status; / / semaphore 对象的 signal 操作函数。 / STATUS PsReleaseSemaphoreObject( IN HANDLE Handle, IN LONG ReleaseCount, IN PLONG PreviousCount ) STATUS Status; PSEMAPHORE Semaphore; ObDerefObject(SemaphoreObject); return Status; if (ReleaseCount WaitListHead, INFINI
20、TE); 代码行添加一个断点。 6按 F5 继续调试,并立即激活虚拟机窗口查看输出。开始时生产者、消费者都不会被信号量阻 塞,同步执行一段时间后才在断点处中断。 7中断后,查看“调用堆栈”窗口,有 Producer 函数对应的堆栈帧,说明此次调用是从生产者 线程函数进入的。 8在“调用堆栈”窗口中双击 Producer 函数所在的堆栈帧,绿色箭头指向等待 Empty 信号量 的代码行,查看 Producer 函数中变量 i 的值为 14,表示生产者线程正在尝试生产 14 号产品。 9在“ 调用堆 栈” 窗口中 双击 PsWaitForSemaphore 函数的 堆栈 帧,查 看 Empty 信
21、号 量计 数的值为-1,所以会调用 PspWait 函数将生产者线程放入 Empty 信号量的等待队列中进行等待。 10激活虚拟机窗口查看输出的结果。生产了从 0 到 13 的 14 个产品,但是只消费了从 0 到 3 的 4 个产品,所以缓冲池中的 10 个缓冲区就都被占用了,这与之前调试的结果是一致的。 5)释放信号量 1删除所有断点。 2在 eosapp.c 文件的 Consumer 函数中,释放 Empty 信号量的代码行 ReleaseSemaphore(EmptySemaphoreHandle, 1, NULL); 添加一个断点。 3按 F5 继续调试,到断点处中断。 4查看 Co
22、nsumer 函数中变量 i 的值为 4,说明已经消费了 4 号产品。 5按照 3.3.2.2 中的方法使用 F10 和 F11 调试进入 PsReleaseSemaphore 函数。 6查看 PsReleaseSemaphore 函数中 Empty 信号量计数的值为-1,和生 产者 线程被阻塞时的值是一致的。 7按 F10 单步调试 PsReleaseSemaphore 函数,直到在代码行 PspWakeThread(&Semaphore-WaitListHead, STATUS_SUCCESS); 处中断。此时 Empty 信号量计数的值已经由-1 增加为了 0,需要调用 PspWakeT
23、hread 函数唤醒阻 塞在 Empty 信号量等待队列中的生产者线程,然后调用 PspSchedule 函数执 行调度,这样生产者线程就得以继续执行。 6)验证生产者线程被唤醒后,是从之前被阻塞时的状态继续执行的: 1在 semaphore.c 文件中 PsWaitForSemaphore 函数的最后一行代码处添加一个断点。 2按 F5 继续调试,在断点处中断。 3查看 PsWaitForSemaphore 函数中 Empty 信号量计数的值为 0,和生产者线 4在“调用堆栈”窗口中可以看到是由 Producer 函数进入的。激活 Producer 函数的堆栈帧,查看 程被唤醒时的值是一致的
24、。 Producer 函数中变量 i 的值为 14,表明之前被阻塞的、正在尝试生产 14 号产品的生产者线程已 经从 PspWait 函数返回并继续执行了。 5结束此次调试。 1)修改 PsWaitForSemaphore 函数 2)修改 PsReleaseSemaphore 函数 3)使用修改完毕的 EOS Kernel 项目生成完全版本的 SDK 文件夹,并覆盖之前的生产者消费者应修改 EOS 的信号量算法 用程序项目的 SDK 文件夹。 4)按 F5 调试执行原有的生产者消费者应用程序项目,结果必须仍然与图 13-2 一致。如果有错误,可以调试内核代码来查找错误,然后在内核项目中修改,并
25、重复步骤 1。 5)将 Producer 函数中等待 Empty 信号量的代码行 WaitForSingleObject(EmptySemaphoreHandle, INFINITE); 替换为 while(WAIT_TIMEOUT = WaitForSingleObject(EmptySemaphoreHandle, 300) WaitForSingleObject(FullSemaphoreHandle, INFINITE); 替换为 while(WAIT_TIMEOUT = WaitForSingleObject(FullSemaphoreHandle, 300) printf(Cons
26、umer wait for full semaphore timeoutn); printf(Producer wait for empty semaphore timeoutn); 6)将 Consumer 函数中等待 Full 信号量的代码行 7)启动调试新的生产者消费者项目,查看在虚拟机中输出的结果,验证信号量超时等待功能是否 能够正常执行。如果有错误,可以调试内核代码来查找错误,然后在内核项目中修改,并重复步骤 1。 8)如果超时等待功能已经能够正常执行,可以考虑将消费者线程修改为一次消费两个产品,来测试 ReleaseCount 参数是否能够正常使用。使用实验文件夹中 NewConsumer.c 文件中的 Consumer 函 数替换原有的 Consumer函数 9)一次性消费一个产品 10)一次性消费两个产品 四、实验体会 这次实验里我增强了对于进程同步算法的理解,对书上的消费者生产者模型有了更深的认识,并且留下了新的印象。这次实验提高了我的编程能力和各方面动手能力,让我感受到进程相关内容编程的风格和方法。