哈尔滨工程大学操作系统实验四进程的同步.docx
- 文档编号:25049976
- 上传时间:2023-06-04
- 格式:DOCX
- 页数:22
- 大小:127.74KB
哈尔滨工程大学操作系统实验四进程的同步.docx
《哈尔滨工程大学操作系统实验四进程的同步.docx》由会员分享,可在线阅读,更多相关《哈尔滨工程大学操作系统实验四进程的同步.docx(22页珍藏版)》请在冰豆网上搜索。
哈尔滨工程大学操作系统实验四进程的同步
操作系统
实验报告
课程名称
操作系统实验
课程编号
实验项目名称
进程的同步
学号
20
年级
2013
姓名
徐大亮
专业
软件工程
学生所在学院
软件学院
指导教师
刘冈【J
实验室名称地点
计算机软件第二实验室21#427
哈尔滨工程大学
软件学院
第四讲进程的同步
、实验概述
1.实验名称:
进程的同步
2.实验目的:
(1)使用EOS的信号量,编程解决生产者一消费者问题,理解进程同步的意义。
(2)调试跟踪EOS言号量的工作过程,理解进程同步的原理。
(3)修改EOS勺信号量算法,使之支持等待超时唤醒功能(有限等待),加深理解进程同步的原理。
3.实验类型:
验证+设计
4.实验内容
1)准备实验
2)使用EOS的信号量解决生产者-消费者问题
3)调试EOS信号量的工作过程
4)修改EOS的信号量算法
、实验环境
操作系统:
windowsxp
编译器:
Bochs模拟器
语言:
C语言
工具:
OSLAB
三、实验过程
1准备实验
按照下面的步骤准备本次实验:
1.启动OSLab。
2.新建一个EOSKernel项目
3.生成EOSKernel项目,从而在该项目文件夹中生成SDK文件夹。
4.新建一个EOS应用程序项目。
5.使用在第3步生成的SDK文件夹覆盖EOS应用程序项目文件夹中的SDK文件夹。
2使用EOS的信号量解决生产者-消费者问题
在本实验文件夹中,提供了使用EOS的信号量解决生产者-消费者问题的参考源代码文件pc.c。
使用
OSLab打开此文件(将文件拖动到OSLab窗口中释放即可打开),仔细阅读此文件中的源代码和注释,各
个函数的流程图可以参见图13-1。
思考在两个线程函数(Producer和Consumer)中,哪些是临界资源?
哪些代码是临界区?
哪些代码是进入临界区?
哪些代码是退出临界区?
进入临界区和退出临界区的代码
是否成对出现?
按照下面的步骤查看生产者-消费者同步执行的过程:
1.使用pc.c文件中的源代码,替换之前创建的EOS应用程序项目中EOSApp.c文件内的源代码。
2.按F7生成修改后的EOS应用程序项目。
3.按F5启动调试。
OSLab会首先弹出一个调试异常对话框。
4.在调试异常对话框中选择“否”,继续执行。
5.立即激活虚拟机窗口查看生产者-消费者同步执行的过程,如图13-2。
6.待应用程序执行完毕后,结束此次调试。
3调试EOS信号量的工作过程
3.1创建信号量
信号量结构体(SEMAPHORE)中的各个成员变量是由API函数
CreateSemaphoreB勺对应参数初始化的,
查看main函数中创建Empty和Full信号量使用的参数有哪些不同,又有哪些相同,思考其中的原因。
按照下面的步骤调试信号量创建的过程:
1.按F5启动调试EOS应用项目。
OSLab会首先弹出一个调试异常对话框。
2.在调试异常对话框中选择“是”,调试会中断。
3.在main函数中创建Empty信号量的代码行(第77行)
EmptySemaphoreHandle=CreateSemaphore(BUFFER_SIZE,BUFFER_SIZE,
NULL);
添加一个断点。
4.按F5继续调试,到此断点处中断。
5.按F11调试进入CreateSemaphore函数。
可以看到此API函数只是调用了
EOS内核中的
PsCreateSemaphoreObjec函数来创建信号量对象。
6.按F11调试进入semaphore.c文件中的PsCreateSemaphoreObject函数。
在此函数中,会在EOS
内核管理的内存中创建一个信号量对象(分配一块内存),而初始化信号量对象中各个成员的操作
是在PsInitializeSemaphore函数中完成的。
7.在semaphore.c文件的顶部查找到PsInitializeSemaphore函数的定义(第19
行),在此函数的
第一行(第39行)代码处添加一个断点。
8.按F5继续调试,到断点处中断。
观察PsInitializeSemaphore函数中用来初始
化信号量结构体成
员的值,应该和传入CreateSemaphore函数的参数值是一致的。
9.按F10单步调试PsInitializeSemaphore函数执行的过程,查看信号量结构
体被初始化的过程。
打开“调用堆栈”窗口,查看函数的调用层次。
3.2等待、释放信号量
3.2.1等待信号量(不阻塞)生产者和消费者刚开始执行时,用来放产品的缓冲区都是空的,所以生产者在第一次调用
WaitForSingleObject函数等待Empty信号量时,应该不需要阻塞就可以立即返回。
按照下面的步骤调试:
1.删除所有的断点(防止有些断点影响后面的调试)。
2.在eosapp.c文件的Producer函数中,等待Empty信号量的代码行(第144行)
WaitForSingleObject(EmptySemaphoreHandle,INFINITE);
添加一个断点。
3.按F5继续调试,到断点处中断。
4.WaitForSingleObject函数最终会调用内核中的PsWaitForSemaphore函数完
成等待操作。
所以,
在semaphore.c文件中PsWaitForSemaphore函数的第一行(第68行)添加一个断点。
5.按F5继续调试,到断点处中断。
6.按F10单步调试,直到完成PsWaitForSemaphore函数中的所有操作。
可以看到此次执行并没有
进行等待,只是将Empty信号量的计数减少了1(由10变为了9)就返回了。
3.2.2释放信号量(不唤醒)
1.删除所有的断点(防止有些断点影响后面的调试)。
2.在eosapp.c文件的Producer函数中,释放Full信号量的代码行(第152行)
ReleaseSemaphore(FullSemaphoreHandle,1,NULL);
添加一个断点。
3.按F5继续调试,到断点处中断。
4.按F11调试进入ReleaseSemaphore函数。
5.继续按F11调试进入PsReleaseSemaphoreObjec函数。
6.先使用F10单步调试,当黄色箭头指向第269行时使用F11单步调试,进
入PsReleaseSemaphore
函数。
7.按F10单步调试,直到完成PsReleaseSemaphore函数中的所有操作。
可以看到此次执行没有唤
醒其它线程(因为此时没有线程在Full信号量上被阻塞),只是将Full信号量的计数增加了1
(由0变为了1)。
生产者线程通过等待Empty信号量使空缓冲区数量减少了1,通过释放Full信号量使满缓冲区数量增
加了1,这样就表示生产者线程生产了一个产品并占用了一个缓冲区。
3.2.3等待信号量(阻塞)
由于开始时生产者线程生产产品的速度较快,而消费者线程消费产品的速度较慢,所以当缓冲池中所
有的缓冲区都被产品占用时,生产者在生产新的产品时就会被阻塞,下面调试这种情况。
1.结束之前的调试。
2.删除所有的断点。
3.按F5重新启动调试。
OSLab会首先弹出一个调试异常对话框。
4.在调试异常对话框中选择“是”,调试会中断。
5.在semaphore.c文件中的PsWaitForSemaphore函数的
PspWait(&Semaphore->WaitListHead,INFINITE);
代码行(第78行)添加一个断点。
6.按F5继续调试,并立即激活虚拟机窗口查看输出。
开始时生产者、消费者都不会被信号量阻塞,
同步执行一段时间后才在断点处中断。
7.中断后,查看“调用堆栈”窗口,有Producer函数对应的堆栈帧,说明此次调用是从生产者线
程函数进入的。
8.在“调用堆栈”窗口中双击Producer函数所在的堆栈帧,绿色箭头指向等待Empty信号量的代
码行,查看Producer函数中变量i的值为14,表示生产者线程正在尝试生产14号产品。
9.在“调用堆栈”窗口中双击PsWaitForSemaphore函数的堆栈帧,查看Empty信号量计数
(Semaphore->Coun)的值为-1,所以会调用PspWait函数将生产者线程放入Empty信号量的等
待队列中进行等待(让出CPU)。
10.激活虚拟机窗口查看输出的结果。
生产了从0到13的14个产品,但是只消费了从0到3的4个
产品,所以缓冲池中的10个缓冲区就都被占用了,这与之前调试的结果是一致的。
3.2.4释放信号量(唤醒)
只有当消费者线程从缓冲池中消费了一个产品,从而产生一个空缓冲区后,生产者线程才会被唤醒并
继续生产14号产品。
可以按照下面的步骤调试:
1.删除所有断点。
2.在eosapp.c文件的Consumer函数中,释放Empty信号量的代码行(第180行)
ReleaseSemaphore(EmptySemaphoreHandle,1,NULL);
添加一个断点。
3.按F5继续调试,到断点处中断。
4.查看Consumer函数中变量i的值为4,说明已经消费了4号产品。
5.按照332.2中的方法使用F10和F11调试进入PsReleaseSemaphor函数。
6.查看PsReleaseSemaphore函数中Empty信号量计数(Semaphore->Coun)的值为-1,和生产者
线程被阻塞时的值是一致的。
7.按F10单步调试PsReleaseSemaphor函数,直到在代码行(第132行)PspWakeThread(&Semaphore->WaitListHead,STATUS_SUCCESS);
处中断。
此时Empty信号量计数的值已经由-1增加为了0,需要调用
PspWakeThreac函数唤醒阻
塞在Empty信号量等待队列中的生产者线程(放入就绪队列中),然后调用
PspSchedule函数执
行调度,这样生产者线程就得以继续执行。
按照下面的步骤验证生产者线程被唤醒后,是从之前被阻塞时的状态继续执行
的:
1.在semaphore.c文件中PsWaitForSemaphore函数的最后一行(第83行)代码处添加一个断点。
2.按F5继续调试,在断点处中断。
3.查看PsWaitForSemaphore函数中Empty信号量计数
(Semaphore->Coun)t的值为0,和生产者线程被唤醒时的值是一致的。
4.在“调用堆栈”窗口中可以看到是由Producer函数进入的。
激活Producer函数的堆栈帧,查看
Producer函数中变量i的值为14,表明之前被阻塞的、正在尝试生产14号产品的生产者线程已
经从PspWait函数返回并继续执行了。
5.结束此次调试。
4•修改EOS勺信号量算法
1)根据文档修改的代码如下:
PsWaitForSemaphore函数代码:
STATUS
PsWaitForSemaphore(
INPSEMAPHORESemaphore,
INULONGMilliseconds
)
/*++
功能描述:
信号量的Wait操作(P操作)。
参数:
Semaphore--Wait操作的信号量对象。
Milliseconds--等待超时上限,单位毫秒。
返回值:
STATUS_SUCCE。
SS
当你修改信号量使之支持超时唤醒功能后,如果等待超时,应该返回
STATUS_TIMEOJJT
--*/
{
BOOLIntState;
STATUSStatus;
中断环境下不能调用此函数
ASSERT(KeGetIntNesting()==0);//
IntState=KeEnableInterrupts(FALSE);//开始原子操作,禁止中断。
//
//目前仅实现了标准记录型信号量,不支持超时唤醒功能,所以PspWait函数
//的第二个参数的值只能是INFINITE。
//
if(Semaphore->Count>0)
{
Semaphore->Count--;
Status=STATUS_SUCCESS;
}
else{
Status=PspWait(&Semaphore->WaitListHead,Milliseconds);
}
KeEnableInterrupts(IntState);//原子操作完成,恢复中断。
returnStatus;
}
PsReleaseSemaphord^码如下:
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。
//
for(Semaphore->Count+=ReleaseCount;
Semaphore->Count>0&&!
ListIsEmpty(&Semaphore->WaitListHead);
Semaphore->Count--){
PspWakeThread(&Semaphore->WaitListHead,STATUS_SUCCESS);
PspThreadSchedule();
}
//Semaphore->Count++;
/*if(Semaphore->Count<=0){
PspWakeThread(&Semaphore->WaitListHead,STATUS_SUCCESS);
}*/
//
//可能有线程被唤醒,执行线程调度。
//
//PspThreadSchedule();
Status=STATUS_SUCCESS;
}
KeEnableInterrupts(IntState);//原子操作完成,恢复中断。
returnStatus;
}
结果如下:
2)根据测试要求1将原来的代码更改为如下:
//
//生产者线程函数。
//
ULONGProducer(PVOIDParam)
{
inti;
intInIndex=0;
for(i=0;i WaitForSingleObject(EmptySemaphoreHandle,INFINITE); WaitForSingleObject(MutexHandle,INFINITE); printf("Producea%d\n",i); Buffer[InIndex]=i; InIndex=(InIndex+1)%BUFFER_SIZE; ReleaseMutex(MutexHandle); ReleaseSemaphore(FullSemaphoreHandle,3,NULL); // //休息一会。 每500毫秒生产一个数。 // Sleep(500); } return0; } // //消费者线程函数。 // ULONGConsumer(PVOIDParam) { inti; intOutIndex=0; for(i=0;i WaitForSingleObject(FullSemaphoreHandle,INFINITE); WaitForSingleObject(MutexHandle,INFINITE); printf("\t\t\tConsumea%d\n",Buffer[OutIndex]); OutIndex=(OutIndex+1)%BUFFER_SIZE; ReleaseMutex(MutexHandle); ReleaseSemaphore(EmptySemaphoreHandle,3,NULL); // //休息一会儿。 让前10个数的消费速度比较慢,后面的较快。 // if(i<10){ Sleep(2000); }else{ Sleep(100); } } return0; } 执行结果如下图所示: 05TjahPC一WirrosrifTTimialFCZHRT CflhSflLE-1[Producer[*roduccrProducerPnMincerE*roducer EYoducca fPressuaitfo? waitforwoitforunitfopuaitfor 2C Ctri+nrnlusullclicun^nleenpiyeemaphoretimeoutcpp'lyacsnaphorctificoutcpplqscnaphorctincoutmplijKntirk|ihiii^IInbuilt,enplyEenaphoretineoutConEainea10 wIHflllU Cunsuned11 Crmsimea1Z Cansunea13 Consunca14 Cuiisunca.Ill Producea zi CcmsLheCtmsunc CinisuncCnnsuhf^ Consiine 止1? 10灯药 E*roducca 22 Cunsunc421 Ciinsun*: n CansuncaZ3 ZB Producea 3)将cunsumer的函数替换,测试一次消费两个产品的情况,代码如下 // //消费者线程函数。 // ULONGConsumer(PVOIDParam) 4){ inti; intOutIndex=0; for(i=0;i while(WAIT_TIMEOUT== WaitForSingleObject(FullSemaphoreHandle,300)){ printf("Consumerwaitforfullsemaphoretimeout\n"); } while(WAIT_TIMEOUT== WaitForSingleObject(FullSemaphoreHandle,300)){ printf("Consumerwaitforfullsemaphoretimeout\n"); } WaitForSingleObject(MutexHandle,INFINITE);printf("\t\t\tConsumea%d\n",Buffer[OutIndex]); OutIndex=(OutIndex+1)%BUFFER_SIZE; printf("\t\t\tConsumea%d\n",Buffer[OutIndex]); OutIndex=(OutIndex+1)%BUFFER_SIZE; ReleaseMutex(MutexHandle); ReleaseSemaphore(EmptySemaphoreHandle,2,NULL); ////休息一会儿。 让前14个数的消费速度比较慢,后面的较快。 // if(i<14){ Sleep(2000); }else{ Sleep(100); } } return0; } 操作结果如下: 5.思考题 1)PsWaitForSemaphore函数的流程图: PsReleaseSemaphor函数的流程图: ■th估呂杞賣询 1 6.思考题 P143,生产者在生产了13号产品后本来要继续生产14号产品,可此时生产者为什么必须等待消费者消费了4号产品后,才能生产14号产品呢? 生产者和消费者是怎样使用同步对象来实现该同步过程的呢? 答: 因为此时生产者生产了14个产品,而消费者消费了4个产品,即缓冲区有10个资源,已经满了,所以只能等待4号产品消费了才可以继续生产。 四•实验体会 实验过程中遇到的问题主要是在找到需要改的两个函数的过程,开始始终在 app工程中修改sdk代码,导致无论如何编译都不能获得更改的效果,等我一步步寻找后,最终发现该问题,并成功修改函数,让我对整个进程的创建过程,以及进程的等待和释放有了更好地了解,对了大于300ms等待有了较深的理解,并能够实现同时释放多个资源的效果,对操作系统的流程,有了更深地理解。
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 哈尔滨工程 大学 操作系统 实验 进程 同步