061324张耀第五讲 进程的同步.docx
《061324张耀第五讲 进程的同步.docx》由会员分享,可在线阅读,更多相关《061324张耀第五讲 进程的同步.docx(16页珍藏版)》请在冰豆网上搜索。
061324张耀第五讲进程的同步
操作系统
实验报告
课程名称
操作系统实验
课程编号
0906553
实验项目名称
进程的同步
学号
2011061324
年级
2011
姓名
张耀
专业
计算机科学与技术
学生所在学院
计算机科学与技术
指导教师
印桂生
实验室名称地点
21B276
哈尔滨工程大学
计算机科学与技术学院
第五讲进程的同步
一、实验概述
1.实验名称
进程的同步
2.实验目的
(1)使用EOS的信号量,编程解决生产者—消费者问题,理解进程同步的意义;
(2)调试跟踪EOS信号量的工作过程,理解进程同步的原理;
(3)修改EOS的信号量算法,使之支持等待超时唤醒功能(有限等待),加深理解进程同步的原理。
3.实验类型
验证型和设计型实验
4.实验内容
(1)准备实验;
(2)使用EOS的信号量解决生产者-消费者问题;
(3)调试EOS信号量的工作过程;
创建信号量
等待释放信号量
等待信号量(不阻塞)
释放信号量(不唤醒)
等待信号量(阻塞)
释放信号量(唤醒)
(4)修改EOS的信号量算法。
二、实验环境
(1)OSLab集成实验环境;
(2)EOS操作系统;
(2)C语言。
三、实验过程
1.设计思路和流程图
图3.1.1
2.需要解决的问题及解答
(1)生产者在生产了13号产品后本来要继续生产14号产品,可此时生产者为什么必须等待消费者消费了4号产品后,才能生产14号产品呢?
生产者和消费者是怎样使用同步对象来实现该同步过程的呢?
答:
因为临界资源的访问限制,程序中限定了缓冲池的大小为10,只有缓冲池有空余时生产者才能向里边放产品,同时只有缓冲池有产品时消费者才能向外取东西。
当生产者生产了13号产品后,共生产了从0到13的14个产品,但是只消费了从0到3的4个产品,所以缓冲池中的10个缓冲区就都被占用了,所以不能继续生产14号产品,而要等到消费者消费掉一个产品后,缓冲池有空余位置,才能继续生产14号产品。
当生产者线程生产了13号产品后,此时Full信号量的值为10,而Empty信号量的值为0,此时若生产者线程要再生产一个产品,先对Empty减1,此时Empty值小于零,生产者线程进入等待队列;而此时若有一个消费者线程要消费一个产品,先对Full减1,此时Full值为9,大于0,如果没有线程占用缓冲池,消费者可以消费一个产品。
这样,生产者和消费者就能实现同步过程了。
(2)生产者线程和消费者线程是如何使用Mutex、Empty信号量和Full信号量来实现同步的?
在两个线程函数中对这三个同步对象的操作能够改变顺序吗?
答:
Mutex、Empty、Full三个信号量的初始值分别为1、10、0,当存在一个生产者线程访问缓冲池时,首先对Empty减1,如果大于0,则说明还有剩余缓冲区可以让生产者放入产品,否则生产者线程进入等待队列;再对Mutex减1,如果大于等于0,则说明没有线程占用缓冲池,否则生产者线程进入等待队列。
生产完产品后,对Mutex加1,解除封锁;再对Full加1,说明生产了一个产品占用了一个缓冲区。
消费者线程同理,对信号量的操作顺序与生产者线程相反。
不能对这三个同步对象的操作改变顺序,否则可能造成死锁。
(3)思考在ps/semaphore.c文件内的PsWaitForSemaphore和PsReleaseSemaphore函数中,为什么要使用原子操作?
答:
在执行PsWaitForSemaphore和PsReleaseSemaphore函数的时候,不允许cpu响应外部中断,如果此时cpu响应了外部中断,会产生不可预料的结果,无法正常完成函数的功能。
(4)绘制ps/semaphore.c文件内PsWaitForSemaphore和PsReleaseSemaphore函数的流程图。
PsWaitForSemaphore流程图:
图3.2.1
PsReleaseSemaphore函数的流程图:
图3.2.2
(5)根据本实验3.3.2节中设置断点和调试的方法,自己设计一个类似的调试方案来验证消费者线程在消费24号产品时会被阻塞,直到生产者线程生产了24号产品后,消费者线程才被唤醒并继续执行的过程。
答:
调试方案如下:
①删除所有的断点。
②按F5启动调试。
OSLab会首先弹出一个调试异常对话框。
③在调试异常对话框中选择“是”,调试会中断。
④在Consumer函数中等待Full信号量的代码行(第173行)WaitForSingleObject(FullSemaphoreHandle,INFINITE);
添加一个断点。
⑤在“断点”窗口(按Alt+F9打开)中此断点的名称上点击右键。
⑥在弹出的快捷菜单中选择“条件”。
⑦在“断点条件”对话框(按F1获得帮助)的表达式编辑框中,输入表达式“i==24”。
⑧点击“断点条件”对话框中的“确定”按钮。
⑨按F5继续调试。
只有当消费者线程尝试消费24号产品时才会在该条件断点处中断。
3.主要数据结构、实现代码及其说明
修改PsWaitForSemaphore函数:
if(Semaphore->Count>0)//如果信号量大于零,说明尚有资源,可以为线程分配
Semaphore->Count--;
else//否则,说明资源数量不够,不能再为线程分配资源,因此要使线程等待
if(Semaphore->Count==0)s=PspWait(&Semaphore->WaitListHead,Milliseconds);
修改PsReleaseSemaphore函数:
if(ReleaseCount>0)//ReleaseCount大于0才执行释放操作
{
Semaphore->Count++;
while((!
ListIsEmpty(&Semaphore->WaitListHead))&&(ReleaseCount)){
PspWakeThread(&Semaphore->WaitListHead,STATUS_SUCCESS);//唤醒线程
PspThreadSchedule();//线程调度
ReleaseCount--;
}//循环唤醒线程
Semaphore->Count=Semaphore->Count+ReleaseCount;
Status=STATUS_SUCCESS;
}elseprintf("Error:
ReleaseCountisnegative!
");
}//否侧输出ReleaseCount值错误
修改Producer函数:
WaitForSingleObject(EmptySemaphoreHandle,INFINITE);
替换为
while(WAIT_TIMEOUT==WaitForSingleObject(EmptySemaphoreHandle,300)){
printf("Producerwaitforemptysemaphoretimeout\n");
}//超时300ms则输出Producerwaitforemptysemaphoretimeout
修改Consumer函数:
WaitForSingleObject(FullSemaphoreHandle,INFINITE);
替换为
while(WAIT_TIMEOUT==WaitForSingleObject(FullSemaphoreHandle,300)){
printf("Consumerwaitforfullsemaphoretimeout\n");
}//超时300ms则输出Consumerwaitforfullsemaphoretimeout
4.源程序并附上注释
STATUS
PsWaitForSemaphore(
INPSEMAPHORESemaphore,
INULONGMilliseconds
)
/*++
功能描述:
信号量的Wait操作(P操作)。
参数:
Semaphore--Wait操作的信号量对象。
Milliseconds--等待超时上限,单位毫秒。
返回值:
STATUS_SUCCESS。
当你修改信号量使之支持超时唤醒功能后,如果等待超时,应该返回STATUS_TIMEOUT。
--*/
{
BOOLIntState;
STATUSs=STATUS_SUCCESS;
ASSERT(KeGetIntNesting()==0);//中断环境下不能调用此函数。
IntState=KeEnableInterrupts(FALSE);//开始原子操作,禁止中断。
//
//目前仅实现了标准记录型信号量,不支持超时唤醒功能,所以PspWait函数
//的第二个参数的值只能是INFINITE。
//
//Semaphore->Count--;
//if(Semaphore->Count<0){
//PspWait(&Semaphore->WaitListHead,INFINITE);
//}
if(Semaphore->Count>0)//如果信号量大于零,说明尚有资源,可以为线程分配
Semaphore->Count--;
else//否则,说明资源数量不够,不能再为线程分配资源,因此要使线程等待
if(Semaphore->Count==0)s=PspWait(&Semaphore->WaitListHead,Milliseconds);
KeEnableInterrupts(IntState);//原子操作完成,恢复中断。
//returnSTATUS_SUCCESS;
returns;
}
STATUS
PsReleaseSemaphore(
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;
}
//
//目前仅实现了标准记录型信号量,每执行一次信号量的释放操作
//只能使信号量的值增加1。
//
//Semaphore->Count++;
//if(Semaphore->Count<=0){
//PspWakeThread(&Semaphore->WaitListHead,STATUS_SUCCESS);
//}
if(ReleaseCount>0)//ReleaseCount大于0才执行释放操作
{
Semaphore->Count++;
while((!
ListIsEmpty(&Semaphore->WaitListHead))&&(ReleaseCount)){
PspWakeThread(&Semaphore->WaitListHead,STATUS_SUCCESS);//唤醒线程
PspThreadSchedule();//线程调度
ReleaseCount--;
}//循环唤醒线程
Semaphore->Count=Semaphore->Count+ReleaseCount;
Status=STATUS_SUCCESS;
}elseprintf("Error:
ReleaseCountisnegative!
");
}//否侧输出ReleaseCount值错误
KeEnableInterrupts(IntState);//原子操作完成,恢复中断。
returnStatus;
}
5.程序运行时的初值和运行结果
修改PsWaitForSemaphore函数,先用计数值和0比较,当计数值大于0时,将计数值减1后直接返回成功;当计数值等于0时,调用PspWait函数阻塞线程的执行(将参数Milliseconds做为PspWait函数的第二个参数,并使用PspWait函数的返回值做为返回值)。
如图3.1所示。
图3.5.1
修改PsReleaseSemaphore函数,编写一个使用ReleaseCount做为计数器的循环体,在循环体中完成下面的工作:
1.如果被阻塞的线程数量大于等于ReleaseCount,则循环结束后,有ReleaseCount个线程会被唤醒,而且信号量计数的值仍然为0;
2.如果被阻塞的线程数量(可以为0)小于ReleaseCount,则循环结束后,所有被阻塞的线程都会被唤醒,并且信号量的计数值=ReleaseCount-之前被阻塞线程的数量+之前信号量的计数值。
如图3.2所示。
图3.5.2
使用修改完毕的EOSKernel项目生成完全版本的SDK文件夹,并覆盖之前的生产者-消费者应用程序项目的SDK文件夹。
按F5调试执行原有的生产者-消费者应用程序项目,结果与原来一致,如下图所示。
图3.5.3
修改Producer函数中等待Empty信号量的代码和Consumer函数中等待Full信号量的代码,如图3.4所示。
图3.4
启动调试新的生产者-消费者项目,查看在虚拟机中输出的结果,如图3.5和图3.6所示。
图3.5
图3.6
将消费者线程修改为一次消费两个产品,来测试ReleaseCount参数是否能够正常使用,如图3.7和图3.8所示。
图3.7
图3.8
四、实验体会
本次实验中,通过对EOS操作系统的P、V操作的调试,以及对源码的阅读,让我更加深入的理解了一个真正运行的操作系统中是如何通过信号量以及P、V操作来解决进程同步问题的。
本次实验的难点在于修改PsReleaseSemaphore函数使其能批量释放,实验材料中给出了一个解决方案,刚开始并没有更多思考,直接按PDF中的方案走,运行编写的程序后始终不能达到预期的效果。
后来我才重头捋了一遍生产者和消费者的同步过程,再根据老师上课时候讲的P、V操作的流程,重新编写程序,这才成功实现了目标。
反过头来对比自己重写的程序,与PDF上的方案其实本质上还是一致的,所以我觉得其实只要是理解到了问题的原理,自己完全能够解决问题。
其次还是程序的健壮性问题,还需要多多练习,才能考虑的周全。