哈尔滨工程大学操作系统 实验四进程的同步.docx

上传人:b****6 文档编号:6934016 上传时间:2023-01-12 格式:DOCX 页数:18 大小:222KB
下载 相关 举报
哈尔滨工程大学操作系统 实验四进程的同步.docx_第1页
第1页 / 共18页
哈尔滨工程大学操作系统 实验四进程的同步.docx_第2页
第2页 / 共18页
哈尔滨工程大学操作系统 实验四进程的同步.docx_第3页
第3页 / 共18页
哈尔滨工程大学操作系统 实验四进程的同步.docx_第4页
第4页 / 共18页
哈尔滨工程大学操作系统 实验四进程的同步.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

哈尔滨工程大学操作系统 实验四进程的同步.docx

《哈尔滨工程大学操作系统 实验四进程的同步.docx》由会员分享,可在线阅读,更多相关《哈尔滨工程大学操作系统 实验四进程的同步.docx(18页珍藏版)》请在冰豆网上搜索。

哈尔滨工程大学操作系统 实验四进程的同步.docx

哈尔滨工程大学操作系统实验四进程的同步

操作系统

实验报告

课程名称

操作系统实验

课程编号

实验项目名称

进程的同步

学号

20

年级

2013

姓名

徐大亮

专业

软件工程

学生所在学院

软件学院

指导教师

刘刚

实验室名称地点

计算机软件第二实验室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函数CreateSemaphore的对应参数初始化的,

查看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内核中的

PsCreateSemaphoreObject函数来创建信号量对象。

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调试进入PsReleaseSemaphoreObject函数。

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->Count)的值为-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.按照3.3.2.2中的方法使用F10和F11调试进入PsReleaseSemaphore函数。

6.查看PsReleaseSemaphore函数中Empty信号量计数(Semaphore->Count)的值为-1,和生产者

线程被阻塞时的值是一致的。

7.按F10单步调试PsReleaseSemaphore函数,直到在代码行(第132行)

PspWakeThread(&Semaphore->WaitListHead,STATUS_SUCCESS);

处中断。

此时Empty信号量计数的值已经由-1增加为了0,需要调用PspWakeThread函数唤醒阻

塞在Empty信号量等待队列中的生产者线程(放入就绪队列中),然后调用PspSchedule函数执

行调度,这样生产者线程就得以继续执行。

按照下面的步骤验证生产者线程被唤醒后,是从之前被阻塞时的状态继续执行的:

1.在semaphore.c文件中PsWaitForSemaphore函数的最后一行(第83行)代码处添加一个断点。

2.按F5继续调试,在断点处中断。

3.查看PsWaitForSemaphore函数中Empty信号量计数(Semaphore->Count)的值为0,和生产者线

程被唤醒时的值是一致的。

4.在“调用堆栈”窗口中可以看到是由Producer函数进入的。

激活Producer函数的堆栈帧,查看

Producer函数中变量i的值为14,表明之前被阻塞的、正在尝试生产14号产品的生产者线程已

经从PspWait函数返回并继续执行了。

5.结束此次调试。

4.修改EOS的信号量算法

1)根据文档修改的代码如下:

PsWaitForSemaphore函数代码:

STATUS

PsWaitForSemaphore(

INPSEMAPHORESemaphore,

INULONGMilliseconds

/*++

功能描述:

信号量的Wait操作(P操作)。

参数:

Semaphore--Wait操作的信号量对象。

Milliseconds--等待超时上限,单位毫秒。

返回值:

STATUS_SUCCESS。

当你修改信号量使之支持超时唤醒功能后,如果等待超时,应该返回STATUS_TIMEOUT。

--*/

{

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;

}

PsReleaseSemaphore代码如下:

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;

}

执行结果如下图所示:

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函数的流程图:

PsReleaseSemaphore函数的流程图:

6.思考题

P143,生产者在生产了13号产品后本来要继续生产14号产品,可此时生产者为什么必须等待消费者消费了4号产品后,才能生产14号产品呢?

生产者和消费者是怎样使用同步对象来实现该同步过程的呢?

答:

因为此时生产者生产了14个产品,而消费者消费了4个产品,即缓冲区有10个资源,已经满了,所以只能等待4号产品消费了才可以继续生产。

四.实验体会

实验过程中遇到的问题主要是在找到需要改的两个函数的过程,开始始终在app工程中修改sdk代码,导致无论如何编译都不能获得更改的效果,等我一步步寻找后,最终发现该问题,并成功修改函数,让我对整个进程的创建过程,以及进程的等待和释放有了更好地了解,对了大于300ms等待有了较深的理解,并能够实现同时释放多个资源的效果,对操作系统的流程,有了更深地理解。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1