视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
进程的同步实验报告
2025-09-26 22:00:40 责编:小OO
文档
操作系统

实   验   报   告

课程名称操作系统实验
实验项目名称进程的同步
学号班级20100612
姓名专业计算机科学与技术
学生所在学院计算机科学与技术学院指导教师
实验室名称地点

计算机基础第七实验室 21B475

                                                       

哈尔滨工程大学

计算机科学与技术学院

一、实验概述

1. 实验名称

进程的同步

2. 实验目的

⑴使用EOS的信号量,编程解决生产者 消费者问题,理解进程同步的意义。 

⑵调试跟踪EOS信号量的工作过程,理解进程同步的原理。 

⑶修改EOS的信号量算法,使之支持等待超时唤醒功能(有限等待),加深理解进程同步的原理。

3. 实验类型

验证+设计

4. 实验内容

⑴准备实验

⑵使用EOS的信号量解决生产者-消费者问题

⑶调试EOS信号量的工作过程

①创建信号量

②等待释放信号量

③等待信号量(不阻塞)

④释放信号量(不唤醒)

⑤等待信号量(阻塞)

⑥释放信号量(唤醒)

⑷修改EOS的信号量算法

二、实验环境

WindowsXP + EOS集成实验环境

三、实验过程

1. 设计思路和流程图

                           图4-1.整体试验流程图

           图4-2.Main函数流程图、生产者消费、消费者流程图

2. 算法实现

3. 需要解决的问题及解答 

(1). 思考在ps/semaphore.c文件内的PsWaitForSemaphore和PsReleaseSemaphore函数中,为什么要使用原子操作? 

答:在执行等待信号量和释放信号量的时候,是不允许cpu响应外部中断的,如果此时cpu响应了外部中断,会产生不可预料的结果,无法正常完成原子操作。

(2). 绘制ps/semaphore.c文件内PsWaitForSemaphore和PsReleaseSemaphore函数的流程图。

(3). P143生产者在生产了13号产品后本来要继续生产14号产品,可此时生产者为什么必须等待消费者消费了4号产品后,才能生产14号产品呢?生产者和消费者是怎样使用同步对象来实现该同步过程的呢?

答:这是因为临界资源的。临界资源就像产品仓库,只有“产品仓库”空闲生产者才能生产东西,有权向里面放东西。所以它必须等到消费者,取走产品,“产品空间”(临界资源)空闲时,才继续生产14号产品。

(4). 根据本实验3.3.2节中设置断点和调试的方法,自己设计一个类似的调试方案来验证消费者线程在消费24号产品时会被阻塞,直到生产者线程生产了24号产品后,消费者线程才被唤醒并继续执行的过程。 

答:可以按照下面的步骤进行调试 

(1) 删除所有的断点。 

(2) 按F5启动调试。OS Lab会首先弹出一个调试异常对话框。 

(3) 在调试异常对话框中选择“是”,调试会中断。 

(4) 在Consumer函数中等待Full信号量的代码行(第173行) WaitForSingleObject(FullSemaphoreHandle, INFINITE); 添加一个断点。 

(5) 在“断点”窗口(按Alt+F9打开)中此断点的名称上点击右键。 

(6) 在弹出的快捷菜单中选择“条件”。 

(7) 在“断点条件”对话框(按F1获得帮助)的表达式编辑框中,输入表达式“i == 24”。 

(8) 点击“断点条件”对话框中的“确定”按钮。 

(9) 按F5继续调试。只有当消费者线程尝试消费24号产品时才会在该条件断点处中断。

4. 主要数据结构、实现代码及其说明

修改PsWaitForSemaphore函数

if (Semaphore->Count>0){

        Semaphore->Count--;

        flag=STATUS_SUCCESS;

    }//如果信号量大于零,说明尚有资源,可以为线程分配

    else 

        flag=PspWait(&Semaphore->WaitListHead, Milliseconds);

KeEnableInterrupts(IntState); // 原子操作完成,恢复中断。

return flag;

}//否则,说明资源数量不够,不能再为线程分配资源,因此要使线程等待

修改PsReleaseSemaphore函数

if (Semaphore->Count + ReleaseCount > Semaphore->MaximumCount) {

Status = STATUS_SEMAPHORE_LIMIT_EXCEEDED;

} else {

        // 记录当前的信号量的值。

        if (NULL != PreviousCount) {

            *PreviousCount = Semaphore->Count;

        }

        int mm=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(

    IN PSEMAPHORE Semaphore,

    IN LONG InitialCount,

    IN LONG MaximumCount)

/*++

功能描述:

    初始化信号量结构体。

参数:

    Semaphore -- 要初始化的信号量结构体指针。

    InitialCount -- 信号量的初始值,不能小于 0 且不能大于 MaximumCount。

    MaximumCount -- 信号量的最大值,必须大于 0。

返回值:无。

--*/

{

ASSERT(InitialCount >= 0 && InitialCount <= MaximumCount && MaximumCount > 0);

Semaphore->Count = InitialCount;

Semaphore->MaximumCount = MaximumCount;

ListInitializeHead(&Semaphore->WaitListHead);

}

STATUS

PsWaitForSemaphore(

    IN PSEMAPHORE Semaphore,

    IN ULONG Milliseconds)

/*++

功能描述:

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

参数:

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

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

返回值:

    STATUS_SUCCESS。

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

--*/

{

    BOOL IntState;

    STATUS flag;

    ASSERT(KeGetIntNesting() == 0); // 中断环境下不能调用此函数。

IntState = KeEnableInterrupts(FALSE); // 开始原子操作,禁止中断。

    // 目前仅实现了标准记录型信号量,不支持超时唤醒功能,所以 PspWait 函数

    // 的第二个参数的值只能是 INFINITE。

if (Semaphore->Count>0){

        Semaphore->Count--;

        flag=STATUS_SUCCESS;

    }

    else 

        flag=PspWait(&Semaphore->WaitListHead, Milliseconds);

KeEnableInterrupts(IntState); // 原子操作完成,恢复中断。

return flag;

}

STATUS

PsReleaseSemaphore(

    IN PSEMAPHORE Semaphore,

    IN LONG ReleaseCount,

    OUT PLONG PreviousCount

    )

/*++

功能描述:

    信号量的 Signal 操作(V 操作)。

参数:

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

    ReleaseCount -- 信号量计数增加的数量。当前只能为 1。当你修改信号量使之支持

                    超时唤醒功能后,此参数的值能够大于等于 1。

    PreviousCount -- 返回信号量计数在增加之前的值。

返回值:

    如果成功释放信号量,返回 STATUS_SUCCESS。

--*/

{

    STATUS Status;

    BOOL IntState;

IntState = KeEnableInterrupts(FALSE); // 开始原子操作,禁止中断。

if (Semaphore->Count + ReleaseCount > Semaphore->MaximumCount) {

Status = STATUS_SEMAPHORE_LIMIT_EXCEEDED;

} else {

        // 记录当前的信号量的值。

        if (NULL != PreviousCount) {

            *PreviousCount = Semaphore->Count;

        }

        int mm=Semaphore->Count;

        // 目前仅实现了标准记录型信号量,每执行一次信号量的释放操作

        // 只能使信号量的值增加 1。

        while ((!ListIsEmpty(&Semaphore->WaitListHead))&&(ReleaseCount)){

            PspWakeThread(&Semaphore->WaitListHead, STATUS_SUCCESS);

            PspThreadSchedule();

            ReleaseCount--;

        }

        Semaphore->Count=mm+ReleaseCount;

        // 可能有线程被唤醒,执行线程调度。

        Status = STATUS_SUCCESS;

    }

KeEnableInterrupts(IntState); // 原子操作完成,恢复中断。

return Status;

}

//////////////////////////////////////////////////////////////////////////

// 下面是和信号量对象类型相关的代码。

// 信号量对象类型指针。

POBJECT_TYPE PspSemaphoreType = NULL;

// 用于初始化 semaphore 结构体的参数结构体。

typedef struct _SEM_CREATE_PARAM{

    LONG InitialCount;

    LONG MaximumCount;

}SEM_CREATE_PARAM, *PSEM_CREATE_PARAM;

// semaphore 对象的构造函数,在创建新 semaphore 对象时被调用。

VOID

PspOnCreateSemaphoreObject(

    IN PVOID SemaphoreObject,

    IN ULONG_PTR CreateParam)

{

    PsInitializeSemaphore( (PSEMAPHORE)SemaphoreObject, 

                           ((PSEM_CREATE_PARAM)CreateParam)->InitialCount,

                           ((PSEM_CREATE_PARAM)CreateParam)->MaximumCount );

}

// semaphore 对象类型的初始化函数。

VOID

PspCreateSemaphoreObjectType(

    VOID)

{

    STATUS Status;

    OBJECT_TYPE_INITIALIZER Initializer;

    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("Failed to create semaphore object type!");

    }

}

// semaphore 对象的构造函数。

STATUS

PsCreateSemaphoreObject(

    IN LONG InitialCount,

    IN LONG MaximumCount,

    IN PSTR Name,

    OUT PHANDLE SemaphoreHandle

    )

{

    STATUS Status;

    PVOID SemaphoreObject;

    SEM_CREATE_PARAM CreateParam;

if(InitialCount < 0 || MaximumCount <= 0 || InitialCount > MaximumCount){

        return STATUS_INVALID_PARAMETER;

    }

    // 创建信号量对象。

    CreateParam.InitialCount = InitialCount;

    CreateParam.MaximumCount = MaximumCount;

    Status = ObCreateObject( PspSemaphoreType,

                             Name,

                             sizeof(SEMAPHORE),

                             (ULONG_PTR)&CreateParam,

                             &SemaphoreObject);

    if (!EOS_SUCCESS(Status)) {

        return Status;

    }

    Status = ObCreateHandle(SemaphoreObject, SemaphoreHandle);

    if (!EOS_SUCCESS(Status)) {

        ObDerefObject(SemaphoreObject);

    }

    return Status;

}

// semaphore 对象的 signal 操作函数。

STATUS

PsReleaseSemaphoreObject(

    IN HANDLE Handle,

    IN LONG ReleaseCount,

    IN PLONG PreviousCount)

{

    STATUS Status;

    PSEMAPHORE Semaphore;

if (ReleaseCount < 1) {

        return STATUS_INVALID_PARAMETER;

    }

    // 由 semaphore 句柄得到 semaphore 对象的指针。

    Status = ObRefObjectByHandle(Handle, PspSemaphoreType, (PVOID*)&Semaphore);

    if (EOS_SUCCESS(Status)) {

        Status = PsReleaseSemaphore(Semaphore, ReleaseCount, PreviousCount);

        ObDerefObject(Semaphore);

    }

    return Status;

}

6. 程序运行时的初值和运行结果

四、实验体会

经过本次实验,初步了解操作系统的进程同步的过程。我对生产者-消费者问题的解决办法有了更全面的认识,同时对调试代码更加熟练。本次实验最大的体会就是,做东西要细心,在写代码的过程中,稍不留意就给后期调试工作带来很多问题。下载本文

显示全文
专题