操作系统实验进程的同步heu.docx
《操作系统实验进程的同步heu.docx》由会员分享,可在线阅读,更多相关《操作系统实验进程的同步heu.docx(23页珍藏版)》请在冰豆网上搜索。
操作系统实验进程的同步heu
操作系统
实验报告
课程名称
操作系统实验
课程编号
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
原子操作前关中断
开始原子操作V操作
返回“信号数目量超出范围”
记录当前信号量的值
信号量值+1
释放信号量
P和V操作的信号量之和大于缓冲队列长度
Y
N
P操作控制的信号量不大于0
Y
唤醒等待进程
返回“唤醒成功”
唤醒队列中进程
被阻塞进程量小于要释放的信号量
等待队列为空
N
Y
N
Y
N
结束
PsReleaseSemaphore
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"
VOID
PsInitializeSemaphore(
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);
}
STATUS
PsWaitForSemaphore(
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;
}
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;
}
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对象时被调用。
//
VOID
PspOnCreateSemaphoreObject(
INPVOIDSemaphoreObject,
INULONG_PTRCreateParam
)
{
PsInitializeSemaphore((PSEMAPHORE)SemaphoreObject,
((PSEM_CREATE_PARAM)CreateParam)->InitialCount,
((PSEM_CREATE_PARAM)CreateParam)->MaximumCount);
}
//
//semaphore对象类型的初始化函数。
//
VOID
PspCreateSemaphoreObjectType(
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对象的构造函数。
//
STATUS
PsCreateSemaphoreObject(
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操作函数。
//
STATUS
PsReleaseSemaphoreObject(
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)创建信号量
按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函数中的所有操作。
可以看到此次执行并没有进行等待,只是将Empty信号量的计数减少了1(由10变为了9)就返回了。
3)释放信号量(不唤醒)
删除所有的断点(防止有些断点影响后面的调试)。
在eosapp.c文件的Producer函数中,释放Full信号量的代码行(第152行)
ReleaseSemaphore(FullSemaphoreHandle,1,NULL);添加一个断点。
按F5继续调试,到断点处中断。
按F11调试进入ReleaseSemaphore函数。
继续按F11调试进入PsReleaseSemaphoreObject函数。
先使用F10单步调试,当黄色箭头指向第269行时使用F11单步调试,进入PsReleaseSemaphore函数。
按F10单步调试,直到完成PsReleaseSemaphore函数中的所有操作。
可以看到此次执行没有唤醒其它线程(因为此时没有线程在Full信号量上被阻塞),只是将Full信号量的计数增加了1(由0变为了1)。
4)等待信号量(阻塞)
结束之前的调试。
删除所有的断点。
按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个缓冲区就都被占用了,这与之前调试的结果是一致的。
5)释放信号量(唤醒)
删除所有断点。
在eosapp.c文件的Consumer函数中,释放Empty信号量的代码行(第180行)
ReleaseSemaphore(EmptySemaphoreHandle,1,NULL);添加一个断点。
按F5继续调试,到断点处中断。
查看Consumer函数中变量i的值为4,说明已经消费了4号产品。
按照3.3.2.2中的方法使用F10和F11调试进入PsReleaseSemaphore函数。
查看PsReleaseSemaphore函数中Empty信号量计数(Semaphore->Count)的值为-1