操作系统实验报告课程名称操作系统实验实验项目名称进程的同步学号班级090614姓名专业计算机科学与技术学生所在学院计算机科学与技术学院指导教师实验室名称地点计算机软件第一实验室哈尔滨工程大学计算机科学与技术学院1一、实验概述1.实验名称进程的同步2.实验目的(1)使用EOS的信号量编程解决生产者—消费者问题,理解进程同步的意义。(2)调试跟踪EOS的信号量的工作过程,理解进程同步的原理。(3)修改EOS的信号量算法,使之支持等待超时唤醒功能(有限等待),加深理解进程同步的原理。3.实验类型验证+设计4.实验内容(1)准备实验启动OSLab。新建一个EOSKernel项目。生成EOSKernel项目,从而在该项目文件夹中生成SDK文件夹。新建一个EOS应用程序项目。使用在第3步生成的SDK文件夹覆盖EOS应用程序项目文件夹中的SDK文件夹。(2)生产者-消费者同步执行的过程使用pc.c文件中的源代码,替换之前创建的EOS应用程序项目中EOSApp.c文件内的源代码。按F7生成修改后的EOS应用程序项目。按F5启动调试。OSLab会首先弹出一个调试异常对话框。在调试异常对话框中选择“否”,继续执行。立即激活虚拟机窗口查看生产者-消费者同步执行的过程。待应用程序执行完毕后,结束此次调试。(3)调试EOS信号量的工作过程创建信号量按F5启动调试EOS应用项目。OSLab会首先弹出一个调试异常对话框。在调试异常对话框中选择“是”,调试会中断。在main函数中创建Empty信号量的代码行(第77行)EmptySemaphoreHandle=CreateSemaphore(BUFFER_SIZE,BUFFER_SIZE,NULL);添加一个断点。按F5继续调试,到此断点处中断。按F11调试进入CreateSemaphore函数。可以看到此API函数只是调用了EOS内核中的PsCreateSemaphoreObject函数来创建信号量对象。按F11调试进入semaphore.c文件中的PsCreateSemaphoreObject函数。在此函数中,会在EOS内核管理的内存中创建一个信号量对象(分配一块内存),而初始化信号量对象中各个成员的操作是在PsInitializeSemaphore函数中完成的。在semaphore.c文件的顶部查找到PsInitializeSemaphore函数的定义(第19行),在此函数的第一行(第39行)代码处添加一个断点。按F5继续调试,到断点处中断。观察PsInitializeSemaphore函数中用来初始化信号量结构体成员的值,应该和传入CreateSemaphore函数的参数值是一致的。按F10单步调试PsInitializeSemaphore函数执行的过程,查看信号量结构体被初始化的过程。打开“调用堆栈”窗口,查看函数的调用层次。等待、释放信号量等待信号量(不阻塞)2删除所有的断点。在eosapp.c文件的Producer函数中,等待Empty信号量的代码行(第144行)WaitForSingleObject(EmptySemaphoreHandle,INFINITE);添加一个断点。按F5继续调试,到断点处中断。WaitForSingleObject函数最终会调用内核中的PsWaitForSemaphore函数完成等待操作。所以,在semaphore.c文件中PsWaitForSemaphore函数的第一行(第68行)添加一个断点。按F5继续调试,到断点处中断。按F10单步调试,直到完成PsWaitForSemaphore函数中的所有操作。释放信号量(不唤醒)删除所有的断点。在eosapp.c文件的Producer函数中,释放Full信号量的代码行(第152行)ReleaseSemaphore(FullSemaphoreHandle,1,NULL);添加一个断点。按F5继续调试,到断点处中断。按F11调试进入ReleaseSemaphore函数。继续按F11调试进入PsReleaseSemaphoreObject函数。先使用F10单步调试,当黄色箭头指向第269行时使用F11单步调试,进入PsReleaseSemaphore函数。按F10单步调试,直到完成PsReleaseSemaphore函数中的所有操作。等待信号量(阻塞)结束之前的调试。删除所有的断点。按F5重新启动调试。OSLab会首先弹出一个调试异常对话框。在调试异常对话框中选择“是”,调试会中断。在semaphore.c文件中的PsWaitForSemaphore函数的PspWait(&Semaphore-WaitListHead,INFINITE);代码行(第78行)添加一个断点。按F5继续调试,并立即激活虚拟机窗口查看输出。开始时生产者、消费者都不会被信号量阻塞,同步执行一段时间后才在断点处中断。中断后,查看“调用堆栈”窗口,有Producer函数对应的堆栈帧,说明此次调用是从生产者线程函数进入的。在“调用堆栈”窗口中双击Producer函数所在的堆栈帧,绿色箭头指向等待Empty信号量的代码行,查看Producer函数中变量i的值为14,表示生产者线程正在尝试生产14号产品。在“调用堆栈”窗口中双击PsWaitForSemaphore函数的堆栈帧,查看Empty信号量计数(Semaphore-Count)的值为-1,所以会调用PspWait函数将生产者线程放入Empty信号量的等待队列中进行等待(让出CPU)。激活虚拟机窗口查看输出的结果。生产了从0到13的14个产品,但是只消费了从0到3的4个产品,所以缓冲池中的10个缓冲区就都被占用了,这与之前调试的结果是一致的。释放信号量(唤醒)删除所有断点。在eosapp.c文件的Consumer函数中,释放Empty信号量的代码行(第180行)ReleaseSemaphore(EmptySemaphoreHandle,1,NULL);添加一个断点。按F5继续调试,到断点处中断。查看Consumer函数中变量i的值为4,说明已经消费了4号产品。按照释放信号量(不唤醒)中的方法使用F10和F11调试进入PsReleaseSemaphore函数。查看PsReleaseSemaphore函数中Empty信号量计数(Semaphore-Count)的值为-1,和生产者线程被阻塞时的值是一致的。按F10单步调试PsReleaseSemaphore函数,直到在代码行(第132行)PspWakeThread(&Semaphore-WaitListHead,STATUS_SUCCESS);处中断。此时Empty信号量计数的值已经由-1增加为了0,需要调用PspWakeThread函数唤醒阻塞在Empty信号量等待队列中的生产者线程(放入就绪队列中),然后调用PspSchedule函数执行调度,这样生产者线程就得以继续执行。验证生产者线程被唤醒后从被阻塞时的状态继续执行在semaphore.c文件中PsWaitForSemaphore函数的最后一行(第83行)代码处添加一个断点。按F5继续调试,3在断点处中断。查看PsWaitForSemaphore函数中Empty信号量计数(Semaphore-Count)的值为0,和生产者线程被唤醒时的值是一致的。在“调用堆栈”窗口中可以看到是由Producer函数进入的。激活Producer函数的堆栈帧,查看Producer函数中变量i的值为14,表明之前被阻塞的、正在尝试生产14号产品的生产者线程已经从PspWait函数返回并继续执行了。结束此次调试。(4)修改EOS的信号量算法修改PsWaitForSemaphore函数先用计数值和0比较,当计数值大于0时,将计数值减1后直接返回成功;当计数值等于0时,调用PspWait函数阻塞线程的执行(将参数Milliseconds做为PspWait函数的第二个参数,并使用PspWait函数的返回值做为返回值)。在函数开始定义一个STATUS类型的变量,用来保存不同情况下的返回值,并在函数最后返回此变量的值。绝不能在原子操作的中途返回!修改PsReleaseSemaphore函数如果被阻塞的线程数量大于等于ReleaseCount,则循环结束后,有ReleaseCount个线程会被唤醒,而且信号量计数的值仍然为0;如果被阻塞的线程数量(可以为0)小于ReleaseCount,则循环结束后,所有被阻塞的线程都会被唤醒,并且信号量的计数值=ReleaseCount-之前被阻塞线程的数量+之前信号量的计数值。(5)测试方法使用修改完毕的EOSKernel项目生成完全版本的SDK文件夹,并覆盖之前的生产者-消费者应用程序项目的SDK文件夹。按F5调试执行原有的生产者-消费者应用程序项目,结果必须仍然与图13-2一致。如果有错误,可以调试内核代码来查找错误,然后在内核项目中修改,将Producer函数中等待Empty信号量的代码行WaitForSingleObject(EmptySemaphoreHandle,INFINITE);替换为while(WAIT_TIMEOUT==WaitForSingleObject(EmptySemaphoreHandle,300)){printf(Producerwaitforemptysemaphoretimeout\n);},将Consumer函数中等待Full信号量的代码行WaitForSingleObject(FullSemaphoreHandle,INFINITE);替换为while(WAIT_TIMEOUT==WaitForSingleObject(FullSemaphoreHandle,300)){printf(Consumerwaitforfullsemaphoretimeout\n);}启动调试新的生产者-消费者项目,查看在虚拟机中输出的结果,验证信号量超时等待功能是否能够正常执行。如果有错误,可以调试内核代码来查找错误,然后在内核项目中修改。如果超时等待功能已经能够正常执行,可以考虑将消费者线程修改为一次消费两个产品,来测试ReleaseCount参数是否能够正常使用。使用实验文件夹中NewConsumer.c文件中的Consumer函数替换原有的Consumer函数。二、实验环境操作系统集成实验环境:OSLab语言:C语言三、实验过程1.设计思路和流程图4main函数开始创建Mutex对象创建empty信号量对象创建full信号量对象创建生产者线程创建消费者线程等待生产者线程和消费者线程结束关闭句柄main函数结束Producer函数开始生产完毕?等待Empty信号量对象等待Mutex对象生产一个产品,占用一个缓冲区循环向后移动缓冲区指针释放Mutex对象释放Full信号量对象等待500毫秒Producer函数结束是否53.需要解决的问题及解答4.主要数据结构、实现代码及其说明修改PsWaitForSemaphore函数if(Semaphore-Count0){Semaphore-Count--;Consumer函数开始消费完毕?等待Full信号量对象等待Mutex对象消费一个产品,清空一个缓冲区循环向后移动缓冲区指针释放Mutex对象释放Empty信号量对象Consumer函数结束是否前10个产品?等待100毫秒等待2000毫秒是否6flag=STATUS_SUCCESS;}//如果信号量大于零,说明尚有资源,可以为线程分配elseflag=PspWait(&Semaphore-WaitListHead,Milliseconds);KeEnableInterrupts(IntState);//原子操作完成,恢复中断。returnflag;}//否则,说明资源数量不够,不能再为线程分配资源,因此要使线程等待修改PsReleaseSemaphore函数if(Semaphore-Count+ReleaseCountSemaphore-Maximum