操作系统实验-进程的同步heu_第1页
操作系统实验-进程的同步heu_第2页
操作系统实验-进程的同步heu_第3页
操作系统实验-进程的同步heu_第4页
操作系统实验-进程的同步heu_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

PAGE操作系统实验报告课程名称操作系统实验课程编号0906553实验项目名称进程的同步学号年级姓名专业学生所在学院指导教师实验室名称地点哈尔滨工程大学计算机科学与技术学院

第六讲进程的同步一、实验概述1.实验名称进程的同步2.实验目的(1)使用EOS的信号量编程解决生产者—消费者问题,理解进程同步的意义。(2)调试跟踪EOS的信号量的工作过程,理解进程同步的原理。(3)修改EOS的信号量算法,使之支持等待超时唤醒功能(有限等待),加深理解进程同步的原理。3.实验类型验证型实验,设计性实验4.实验内容(1)准备实验(2)使用EOS的信号量解决生产者-消费者问题(3)调试EOS信号量的工作过程1)创建信号量2)等待释放信号量3)等待信号量(不阻塞)4)释放信号量(不唤醒)5)等待信号量(阻塞)6)释放信号量(唤醒)(4)修改EOS的信号量算法二、实验环境操作系统集成实验环境OSLab三、实验过程1.设计思路和流程图2.算法实现3.需要解决的问题及解答(1).P143生产者在生产了13号产品后本来要继续生产14号产品,可此时生产者为什么必须等待消费者消费了4号产品后,才能生产14号产品呢?生产者和消费者是怎样使用同步对象来实现该同步过程的呢?答:此时生产了0-13号14个产品,消费了0-3号4个产品,缓冲区都占满了。只有缓冲区有空闲生产者才能生产东西,有权向里面放东西。所以它必须等到消费者,取走产品,有空闲缓冲区时,才继续生产14号产品。(2).P145-3.4修改EOS的信号量算法(只看一次消费1个产品的,一次消费2个产品的可以写到实验报告中)答:见三,四部分(3).思考在ps/semaphore.c文件内的PsWaitForSemaphore和PsReleaseSemaphore函数中,为什么要使用原子操作?答:原子操作要求一旦开始就要运行到结束,不能有中断。在执行等待信号量和释放信号量的时候,不允许cpu响应外部中断,所以使用原子操作。(4).绘制ps/semaphore.c文件内PsWaitForSemaphore和PsReleaseSemaphore函数的流程图。原子操作前关中断PsWaitForSemaphore原子操作前关中断开始原子操作P操作开始原子操作P操作NWait操作的信号量大于0NWait操作的信号量大于0YY执行P操作执行P操作P操作结束P操作结束PsReleaseSemaphore原子操作前关中断原子操作前关中断开始原子操作V操作开始原子操作V操作NP和V操作的信号量之和大于缓冲队列长度NP和V操作的信号量之和大于缓冲队列长度Y记录当前信号量的值Y记录当前信号量的值返回“返回“信号数目量超出范围”释放信号量信号量值+1信号量值+1NP操作控制的信号量不大于0NP操作控制的信号量不大于0N被阻塞进程量小于要释放的信号量YN被阻塞进程量小于要释放的信号量Y唤醒等待进程唤醒等待进程YY唤醒队列中进程唤醒队列中进程NN等待队列为空等待队列为空YY返回“返回“唤醒成功”结束结束4.主要数据结构、实现代码及其说明1)修改PsWaitForSemaphore函数if(Semaphore->Count>0){ Semaphore->Count--; flag=STATUS_SUCCESS; }//如果信号量大于零,说明尚有资源,可以为线程分配 else flag=PspWait(&Semaphore->WaitListHead,Milliseconds);KeEnableInterrupts(IntState);//原子操作完成,恢复中断。returnflag;}//否则,说明资源数量不够,不能再为线程分配资源,因此要使线程等待2)修改PsReleaseSemaphore函数if(Semaphore->Count+ReleaseCount>Semaphore->MaximumCount){Status=STATUS_SEMAPHORE_LIMIT_EXCEEDED;}else{// //记录当前的信号量的值。 // if(NULL!=PreviousCount){ *PreviousCount=Semaphore->Count; } intmm=Semaphore->Count; // //目前仅实现了标准记录型信号量,每执行一次信号量的释放操作 //只能使信号量的值增加1。 // while((!ListIsEmpty(&Semaphore->WaitListHead))&&(ReleaseCount)){ PspWakeThread(&Semaphore->WaitListHead,STATUS_SUCCESS); PspThreadSchedule(); ReleaseCount--; } Semaphore->Count=mm+ReleaseCount; // //可能有线程被唤醒,执行线程调度。 // Status=STATUS_SUCCESS; }5.源程序并附上注释#include"psp.h"VOIDPsInitializeSemaphore( INPSEMAPHORESemaphore, INLONGInitialCount, INLONGMaximumCount )/*++功能描述: 初始化信号量结构体。参数: Semaphore--要初始化的信号量结构体指针。 InitialCount--信号量的初始值,不能小于0且不能大于MaximumCount。 MaximumCount--信号量的最大值,必须大于0。返回值: 无。--*/{ ASSERT(InitialCount>=0&&InitialCount<=MaximumCount&&MaximumCount>0); Semaphore->Count=InitialCount; Semaphore->MaximumCount=MaximumCount; ListInitializeHead(&Semaphore->WaitListHead);}STATUSPsWaitForSemaphore( INPSEMAPHORESemaphore, ININTMilliseconds, INSTATUSi )/*++功能描述: 信号量的Wait操作(P操作)。参数: Semaphore--Wait操作的信号量对象。 Milliseconds--等待超时上限,单位毫秒。返回值: STATUS_SUCCESS。 当你修改信号量使之支持超时唤醒功能后,如果等待超时,应该返回STATUS_TIMEOUT。--*/{ BOOLIntState; ASSERT(KeGetIntNesting()==0);//中断环境下不能调用此函数。 IntState=KeEnableInterrupts(FALSE);//开始原子操作,禁止中断。 // //目前仅实现了标准记录型信号量,不支持超时唤醒功能,所以PspWait函数 //的第二个参数的值只能是INFINITE。 // if(Semaphore->Count>0) {Semaphore->Count--; i=STATUS_SUCCESS; } else { i=PspWait(&Semaphore->WaitListHead,Milliseconds); } KeEnableInterrupts(IntState);//原子操作完成,恢复中断。returni;}STATUSPsReleaseSemaphore( INPSEMAPHORESemaphore, INLONGReleaseCount, OUTPLONGPreviousCount )/*++功能描述: 信号量的Signal操作(V操作)。参数: Semaphore--Wait操作的信号量对象。 ReleaseCount--信号量计数增加的数量。当前只能为1。当你修改信号量使之支持 超时唤醒功能后,此参数的值能够大于等于1。 PreviousCount--返回信号量计数在增加之前的值。返回值: 如果成功释放信号量,返回STATUS_SUCCESS。--*/{ STATUSStatus; BOOLIntState; IntState=KeEnableInterrupts(FALSE);//开始原子操作,禁止中断。 if(Semaphore->Count+ReleaseCount>Semaphore->MaximumCount){ Status=STATUS_SEMAPHORE_LIMIT_EXCEEDED; }else{ // //记录当前的信号量的值。 // if(NULL!=PreviousCount){ *PreviousCount=Semaphore->Count; }INTj=Semaphore->Count; // //目前仅实现了标准记录型信号量,每执行一次信号量的释放操作 //只能使信号量的值增加1。 // while((!ListIsEmpty(&Semaphore->WaitListHead))&&(ReleaseCount)){ PspWakeThread(&Semaphore->WaitListHead,STATUS_SUCCESS); PspThreadSchedule(); ReleaseCount--; } Semaphore->Count=j+ReleaseCount; Status=STATUS_SUCCESS; } KeEnableInterrupts(IntState);//原子操作完成,恢复中断。 returnStatus;}POBJECT_TYPEPspSemaphoreType=NULL;////用于初始化semaphore结构体的参数结构体。//typedefstruct_SEM_CREATE_PARAM{ LONGInitialCount; LONGMaximumCount;}SEM_CREATE_PARAM,*PSEM_CREATE_PARAM;////semaphore对象的构造函数,在创建新semaphore对象时被调用。//VOIDPspOnCreateSemaphoreObject( INPVOIDSemaphoreObject, INULONG_PTRCreateParam ){ PsInitializeSemaphore((PSEMAPHORE)SemaphoreObject, ((PSEM_CREATE_PARAM)CreateParam)->InitialCount, ((PSEM_CREATE_PARAM)CreateParam)->MaximumCount);}////semaphore对象类型的初始化函数。//VOIDPspCreateSemaphoreObjectType( VOID ){ STATUSStatus; OBJECT_TYPE_INITIALIZERInitializer; 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("Failedtocreatesemaphoreobjecttype!"); }}////semaphore对象的构造函数。//STATUSPsCreateSemaphoreObject( INLONGInitialCount, INLONGMaximumCount, INPSTRName, OUTPHANDLESemaphoreHandle ){ STATUSStatus; PVOIDSemaphoreObject; SEM_CREATE_PARAMCreateParam; if(InitialCount<0||MaximumCount<=0||InitialCount>MaximumCount){ returnSTATUS_INVALID_PARAMETER; } // //创建信号量对象。 // CreateParam.InitialCount=InitialCount; CreateParam.MaximumCount=MaximumCount; Status=ObCreateObject(PspSemaphoreType, Name, sizeof(SEMAPHORE), (ULONG_PTR)&CreateParam, &SemaphoreObject); if(!EOS_SUCCESS(Status)){ returnStatus; } Status=ObCreateHandle(SemaphoreObject,SemaphoreHandle); if(!EOS_SUCCESS(Status)){ ObDerefObject(SemaphoreObject); } returnStatus;}////semaphore对象的signal操作函数。//STATUSPsReleaseSemaphoreObject( INHANDLEHandle, INLONGReleaseCount, INPLONGPreviousCount ){ STATUSStatus; PSEMAPHORESemaphore; if(ReleaseCount<1){ returnSTATUS_INVALID_PARAMETER; } //由semaphore句柄得到semaphore对象的指针。 Status=ObRefObjectByHandle(Handle,PspSemaphoreType,(PVOID*)&Semaphore); if(EOS_SUCCESS(Status)){ Status=PsReleaseSemaphore(Semaphore,ReleaseCount,PreviousCount); ObDerefObject(Semaphore); } returnStatus;}6.程序运行时的初值和运行结果(1)准备实验1)启动OSLab。2)新建一个EOSKernel项目。3)生成EOSKernel项目,从而在该项目文件夹中生成SDK文件夹。4)新建一个EOS应用程序项目。5)使用在第3步生成的SDK文件夹覆盖EOS应用程序项目文件夹中的SDK文件夹。(2)使用EOS的信号量解决生产者-消费者问题1)使用pc.c文件中的源代码,替换之前创建的EOS应用程序项目中EOSApp.c文件内的源代码。2)按F7生成修改后的EOS应用程序项目。3)按F5启动调试。OSLab会首先弹出一个调试异常对话框。4)在调试异常对话框中选择“否”,继续执行。5)立即激活虚拟机窗口查看生产者-消费者同步执行的过程。6)待应用程序执行完毕后,结束此次调试。(3)调试EOS信号量的工作过程1)创建信号量eq\o\ac(○,1)按F5启动调试EOS应用项目。OSLab会首先弹出一个调试异常对话框。eq\o\ac(○,2)在调试异常对话框中选择“是”,调试会中断。eq\o\ac(○,3)在main函数中创建Empty信号量的代码行(第77行)EmptySemaphoreHandle=CreateSemaphore(BUFFER_SIZE,BUFFER_SIZE,NULL);添加一个断点。eq\o\ac(○,4)按F5继续调试,到此断点处中断。eq\o\ac(○,5)按F11调试进入CreateSemaphore函数。可以看到此API函数只是调用了EOS内核中的PsCreateSemaphoreObject函数来创建信号量对象。eq\o\ac(○,6)按F11调试进入semaphore.c文件中的PsCreateSemaphoreObject函数。在此函数中,会在EOS内核管理的内存中创建一个信号量对象(分配一块内存),而初始化信号量对象中各个成员的操作是在PsInitializeSemaphore函数中完成的。eq\o\ac(○,7)在semaphore.c文件的顶部查找到PsInitializeSemaphore函数的定义(第19行),在此函数的第一行(第39行)代码处添加一个断点。eq\o\ac(○,8)按F5继续调试,到断点处中断。观察PsInitializeSemaphore函数中用来初始化信号量结构体成员的值,应该和传入CreateSemaphore函数的参数值是一致的。eq\o\ac(○,9)按F10单步调试PsInitializeSemaphore函数执行的过程,查看信号量结构体被初始化的过程。打开“调用堆栈”窗口,查看函数的调用层次。2)等待信号量(不阻塞)eq\o\ac(○,1)删除所有的断点(防止有些断点影响后面的调试)。eq\o\ac(○,2)在eosapp.c文件的Producer函数中,等待Empty信号量的代码行(第144行)WaitForSingleObject(EmptySemaphoreHandle,INFINITE);添加一个断点。eq\o\ac(○,3)按F5继续调试,到断点处中断。eq\o\ac(○,4)WaitForSingleObject函数最终会调用内核中的PsWaitForSemaphore函数完成等待操作。所以,在semaphore.c文件中PsWaitForSemaphore函数的第一行(第68行)添加一个断点。eq\o\ac(○,5)按F5继续调试,到断点处中断。eq\o\ac(○,6)按F10单步调试,直到完成PsWaitForSemaphore函数中的所有操作。可以看到此次执行并没有进行等待,只是将Empty信号量的计数减少了1(由10变为了9)就返回了。3)释放信号量(不唤醒)eq\o\ac(○,1)删除所有的断点(防止有些断点影响后面的调试)。eq\o\ac(○,2)在eosapp.c文件的Producer函数中,释放Full信号量的代码行(第152行)ReleaseSemaphore(FullSemaphoreHandle,1,NULL);添加一个断点。eq\o\ac(○,3)按F5继续调试,到断点处中断。eq\o\ac(○,4)按F11调试进入ReleaseSemaphore函数。eq\o\ac(○,5)继续按F11调试进入PsReleaseSemaphoreObject函数。eq\o\ac(○,6)先使用F10单步调试,当黄色箭头指向第269行时使用F11单步调试,进入PsReleaseSemaphore函数。eq\o\ac(○,7)按F10单步调试,直到完成PsReleaseSemaphore函数中的所有操作。可以看到此次执行没有唤醒其它线程(因为此时没有线程在Full信号量上被阻塞),只是将Full信号量的计数增加了1(由0变为了1)。4)等待信号量(阻塞)eq\o\ac(○,1)结束之前的调试。eq\o\ac(○,2)删除所有的断点。eq\o\ac(○,3)按F5重新启动调试。OSLab会首先弹出一个调试异常对话框。eq\o\ac(○,4)在调试异常对话框中选择“是”,调试会中断。eq\o\ac(○,5)在semaphore.c文件中的PsWaitForSemaphore函数的PspWait(&Semaphore->WaitListHead,INFINITE);代码行(第78行)添加一个断点。eq\o\ac(○,6)按F5继续调试,并立即激活虚拟机窗口查看输出。开始时生产者、消费者都不会被信号量阻塞,同步执行一段时间后才在断点处中断。eq\o\ac(○,7)中断后,查看“调用堆栈”窗口,有Producer函数对应的堆栈帧,说明此次调用是从生产者线程函数进入的。eq\o\ac(○,8)在“调用堆栈”窗口中双击Producer函数所在的堆栈帧,绿色箭头指向等待Empty信号量的代码行,查看Producer函数中变量i的值为14,表示生产者线程正在尝试生产14号产品。eq\o\ac(○,9)在“调用堆栈”窗口中双击PsWaitForSemaphore函数的堆栈帧,查看Empty信号量计数(Semaphore->Count)的值为-1,所以会调用PspWait函数将生产者线程放入Empty信号量的等待队列中进行等待(让出CPU)。eq\o\ac(○,10)激活虚拟机窗口查看输出的结果。生产了从0到13的14个产品,但是只消费了从0到3的4个产品,所以缓冲池中的10个缓冲区就都被占用了,这与之前调试的结果是一致的。5)释放信号量(唤醒)eq\o\ac(○,1)删除所有断点。eq\o\ac(○,2)在eosapp.c文件的Consumer函数中,释放Empty信号量的代码行(第180行)ReleaseSemaphore(EmptySemaphoreHandle,1,NULL);添加一个断点。eq\o\ac(○,3)按F5继续调试,到断点处中断。eq\o\ac(○,4)查看Consumer函数中变量i的值为4,说明已经消费了4号产品。eq\o\ac(○,5)按照3.3.2.2中的方法使用F10和F11调试进入PsReleaseSemaphore函数。eq\o\ac(○,6)查看PsReleaseSemaphore函数中Empty信号量计数(Semaphore->Count)的值为-1,和生产者线程被阻塞时的值是一致的。eq\o\ac(○,7)按F10单步调试PsReleaseSemaphore函数,直到在代码行(第132行)PspWakeThread(&Semaphore->WaitListHead,STATUS_SUCCESS);处中断。此时Empty信号量计数的值已经由-1增加为了0,需要调用PspWakeThread函数唤醒阻塞在Empty信号量等待队列中的生产者线程(放入就绪队列中),然后调用PspSchedule函数执行调度,这样生产者线程就得以继续执行。6)验证生产者线程被唤醒后,是从之前被阻塞时的状态继续执行的:eq\o\ac(○,1)在semaphore.c文件中PsWaitForSemaphore函数的最后一行(第83行)代码处添加一个断点。 eq\o\ac(○,2)按F5继续调试,在断点处中断。 eq\o\ac(○,3)查看PsWaitForSemaphore函数中Empty信号量计数(Semaphore->Count)的值为0,和生产者线程被唤醒时的值

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论