admin 管理员组

文章数量: 887021


2024年1月12日发(作者:paddlepaddle框架是谁开发的)

课程设计(综合实验)报告

( 2015 -- 2016 年度第 1 学期)

名 称: 操作系统综合实验

题 目: oslab综合实验

院 系: 计算机系

班 级:

学 号:

学生姓名:

指导教师:

设计周数: 分散进行

成 绩:

日期: 2015 年 10 月 29 日

一、 综合实验的目的与要求

1. 理解和掌握操作系统的基本概念、基本组成与工作原理;

2. 理解和掌握操作系统中主要功能模块的工作原理及其实现算法;

3. 掌握软件模块设计技能;熟悉并能较好地利用软件开发环境独立编程、调试和分

析程序运行情况,逐渐形成创新思维和从事系统软件的研究和开发能力。

二、实验正文

实验1:实验环境的使用

1.1实验目的:

1.熟悉操作系统集成实验环境OS Lab的基本使用方法。

2.练习编译、调试EOS操作系统内核以及EOS应用程序。

1.2实验内容:

1.启动OS Lab

2.学习OS Lab的基本用法

 新建 Windows 控制台应用程序项目

(1) 在“文件”菜单中选择“新建”,然后单击“项目”。

(2) 在“新建项目”对话框中,选择项目模板“控制台应用程序 (c)”。

(3) 在“名称”中输入新项目使用的文件夹名称“oslab”。

(4) 在“位置”中输入新项目保存在磁盘上的位置“C:test”。

(5) 点击“确定”按钮。

 生成、执行项目

 调试项目(断点中断执行、单步调试、查看变量的值、调用堆栈)

使用断点中断执行

内核项目的生成和调试

 新建 EOS内核项目并按F7生成项目

 调试项目

 查看软盘镜像文件中的内容、EOS SDK(Software Development Kit)文件夹

应用程序项目的生成和调试

 新建 EOS应用程序项目

 生成并调试项目

 查看软盘镜像文件中的内容

 修改 EOS应用程序项目名称

查看变量的值

5.退出OS Lab

6.保存EOS内核项目

1.3思考与练习

 在实验1中,生成EOS SDK文件夹的目的和作用是什么?

答:SDK文件夹中提供了开发EOS应用程序需要的所有文件。debug文件夹是在使用debug配置生成项目时生成的,其中存放了调试版本的EOS二进制文件。release文件夹是在使用release配置生成项目时生成的,其中存放了发布版本的EOS二进制文件(不包含调试信息)。SDK文件夹中的inc文件夹,此文件夹中存放了EOS用于导出API函数和重要数据类型定义的头文件,在编写EOS应用程序时必须包含这些头文件。每次在开发EOS应用程序之前都应该使用EOS Kernel项目的debug配置和release配置来生成EOS Kernel项目,这样才得到完全版本的SDK文件夹供EOS应用程序使用。

实验2:操作系统的启动

2.1实验目的:

1. 跟踪调试EOS在 PC机上从加电复位到成功启动全过程,了解操作系统的启动过程。

2. 查看 EOS 启动后的状态和行为,理解操作系统启动后的工作方式。

2.2实验内容:

1.准备实验:启动OS Lab,新建一个EOS Kenel项目

2.调试EOS操作系统的启动过程:

 使用Bochs作为远程目标机

 调试BIOS程序

(1)启动调试后,Bochs 在 CPU 要执行的第一条指令处中断

(2)在 Console窗口中输入调试命令sreg 后按回车,显示当前CPU中各个段寄存器的值。其中 CS 寄存器信息行中的“s=0xf000”表示 CS 寄存器的值为0xf000。

(3)输入调试命令 r 后按回车,显示当前 CPU 中各个通用寄存器的值,如图 10-3。其中“rip: 0x00000000:0000fff0”表示IP寄存器的值为0xfff0

(4)输入调试命令xp /1024b 0x0000,查看开始的 1024个字节的物理内存。在 Console 中输出的这1K物理内存的值都为0,说明 BIOS中断向量表还没有被加载到此处。输入调试命令 xp /512b 0x7c00,查看软盘引导扇区应该被加载到的内存位置。输出的内存值都为0,说明软盘引导扇区还没有被加载到此处。

 调试软盘引导扇区程序

(1)输入调试命令vb 0x0000:0x7c00,这样就在逻辑地址0x0000:0x7c00处添加了一个断点,输入调试命令 c 继续执行,输入调试命令sreg 验证 CS寄存器的值,输入调试命令r 验证IP 寄存器的值;

(2)输入调试命令 xp /1024b 0x0000 验证此时 BIOS 中断向量表已经被载入,输入调试命令xp /512b 0x7c00 显示软盘引导扇区程序的所有字节;

(3)输入调试命令xp /512b 0x0600 验证图 3-2中第一个用户可用区域是空白的。输入调试命令xp /512b 0x7e00 验证图 3-2中第二个用户可用区域是空白的。自己设计两个查看内存的调试命令,分别验证这两个用户可用区域的高地址端也是空白的。

(4)输入调试命令xp /512b 0xa0000 验证图 3-2中上位内存已经被系统占用。自己设计一个查看内存的调试命令,验证上位内存的高地址端已经被系统占用;

(5)输入调试命令vb 0x0000:0x7d81 添加一个断点,输入调试命令c继续执行,到断点处中断。按照打开 文件的方法打开文件,输入调试命令xp /8b 0x1000查看内存0x1000处的数据,验证此块内存的前三个字节和文件中的第一条指令的字节码是相同的。

(6)根据之前记录的文件的大小,自己设计一个查看内存的调试命令,查看内存中loader程序结束位置的字节码,并与 文件中最后指令的字节码比较,验证

loader 程序被完全加载到了正确的位置。

 调试加载程序:

(1)使用添加物理地址断点的调试命令pb 0x1513添加一个断点,输入调试命令 c 继续执行,到断点处中断。

(2)使用查看虚拟内存的调试命令 x /1wx 0x80001117 查看内存中保存的 32 位函数入口地址。

 调试内核

 EOS启动后的状态和行为

(1)在控制台中输入命令“ver”后按回车,在控制台中输入命令“pt”后按回车

(2)将本实验文件夹中的 文件添加到软盘镜像文件中,待 EOS启动完毕,在EOS控制台中输入命令“hello”后按回车,迅速按 Ctrl+F2切换到控制台2,并输入命令“pt”后按回车。

实验3:进程的创建

3.1实验目的:

1. 练习使用EOS API函数 CreateProcess创建一个进程,掌握创建进程的方法,理解进程和程序的区别;

2. 调试跟踪 CreateProcess函数的执行过程,了解进程的创建过程,理解进程是资源分配的单位。

3.2实验内容:

1.准备实验:启动OS Lab,新建一个EOS Kenel项目,新建一个EOS应用程序项目;

2.练习使用控制台命令创建EOS应用程序的进程;

 将本实验文件夹中的 文件添加到软盘镜像文件中,按F7生成EOS应用项目,按 F5启动调试。忽略异常继续执行应用程序,激活虚拟机窗口,待该应用程序执行完毕后,在EOS 的控制台中输入命令“A:”后回车。 应用程序开始执行;

3.练习通过编程的方式让应用程序创建另一个应用程序的进程

 使用NewProc.c文件中的源代码替换之前创建的EOS应用程序项目中的EOSApp.c文件内的源代码。按 F7生成修改后的EOS应用程序项目。按F5启动调试。激活虚拟机窗口查看应用程序输出的内容。首先开始执行并输出内容,父进程创建了子进程()后,子进程开始执行并输出内容,待子进程结束后父进程再继续执行。

4.调试CreateProcess函数

 按 F5启动调试EOS应用程序,OS Lab首先弹出一个调试异常对话框。 选择“是”调试异常,调试中断。在 main函数中调用CreateProcess函数的代码行添加一个断点。按F5继续调试,在断点处中断。按 F11调试进入CreateProcess函数进行调试

 在“调试”菜单的“窗口”中选择“反汇编”,在“反汇编”窗口中显示CreateProcess函数的指令对应的反汇编代码。在“调用堆栈”窗口中双击 main 函数项,设置 main

函数的调用堆栈帧为活动的。在“反汇编”窗口中查看main 函数的指令所在的虚拟地址都是小于0x80000000,说明应用程序 ()处于低 2G的虚拟地址空间中。 在“调用堆栈”窗口中双击CreateProcess函数项,重新设置CreateProcess函数的调用堆栈帧为活动的。关闭“反汇编”窗口。

5.调试PsCreateProcess函数;

 在 PsCreateProcess函数调用PspCreateProcessEnvironment函数的代码行添加一个断点。按 F5继续调试,按 F11调试进入PspCreateProcessEnvironment函数

 在调用ObCreateObject函数的代码行添加一个断点。按F5继续调试,按 F10执行此函数后中断。将表达式*NewProcess添加到“监视”窗口中。将鼠标移动到“监视”窗口中此表达式的“值”属性上,会弹出一个临时窗口,在临时窗口中会按照进程控制块的结构显示各个成员变量的值,值都为0。

 在代码行NewProcess->Pas = MmCreateProcessAddressSpace()添加一个断点,按 F5继续调试,按 F10执行此行代码后中断。使用F10一步步调试PspCreateProcessEnvironment函数中后面的代码,查看“监视”窗口中*NewProcess

表达式的值。当从 PspCreateProcessEnvironment函数返回到PsCreateProcess函数后,停止按F10。将表达式*ProcessObject添加到“监视”窗口中。继续使用F10一步步调试PsCreateProcess函数中的代码。当调试到PsCreateProcess函数的最后一行代码时,查看进程控制块中的信息,按 F5继续执行。激活虚拟机窗口查看新建进程执行的结果。

6.练习通过编程的方式创建应用程序的多个进程;

 使用NewTwoProc.c文件中的源代码替换 EOS 应用程序项目中 EOSApp.c 文件内的源代码,生成后启动调试,查看多个进程并发执行的结果。

3.3思考与练习

1. 在源代码文件NewTwoProc.c提供的源代码基础上进行修改,要求使用同时创建10个进程。然后尝试调试 PspCreateThread 函数,观察线程控制块(TCB)初始化的过程。

3.在实验3中,在PsCreateProcess函数中调用PspCreateProcessEnvironment函数后又先后调用了PspLoadProcessImage和PspCreateThread函数,分析这些函数的主要功能。能够交换这些函数被调用的顺序吗?为什么?

答:不能调换顺序;创建进程最主要的操作就是创建进程控制块(PCB),并初始化其中的各种信息(也就是为进程分配各种资源)。所以在PsCreateProcess函数中首先调用了PspCreateProcessEnvironmen函数来创建进程控制块。因为PspCreateProcessEnvironment函数的主要功能是创建进程控制块并初始化其中的部分信息, 并且为进程创建了地址空间和分配了句柄PspLoadProcessImage是将进程的可执行映像加载到了进程的地址空间中。PspCreateThread创建了进程的主线程。这三个函数被调用的顺序是不能够改变的,就向上面描述的,加载可执行映像之前必须已经为进程创建了地址空间,这样才能够确定可执行映像可以被加载到内存的什么位置;在创建主线程之前必须已经加载了可执行映像,这样主线程才能够知道自己要从哪里开始执行,执行哪些指令。

实验4:线程的状态和转换

4.1实验目的:

1. 调试线程在各种状态间的转换过程,熟悉线程的状态和转换

2. 通过为线程增加挂起状态,加深对线程状态的理解

4.2实验内容:

1.准备实验:启动OS Lab,新建一个EOS Kenel项目;

2.调试线程状态的转换过程:

 Loop命令:按F7生成实验3.1中创建的EOS Kernel项目,按F5调试然后输入命令“loop”后按回车,结束调试。

 调试线程状态转换的过程:在 ke/sysproc.c文件的LoopThreadFunction函数中,开始死循环的代码行(第787行)添加一个断点。按 F5启动调试。待 EOS启动完毕,在EOS控制台中输入命令“loop”后按回车。删除所有断点。打开ps/sched.c文件,在与线程状态转换相关的函数中添加断点,按F5继续执行。

 线程由阻塞状态进入就绪状态:

(1)在虚拟机窗口中按下一次空格键,EOS 会在 PspUnwaitThread 函数断点处中断。

(2)在“调试”菜单中选择“快速监视”,在快速监视对话框的表达式编辑框中输入表达式“*Thread”,然后点击“重新计算”按钮,即可查看线程控制块(TCB)中的

其中 State域的值为3(Waiting) ,双向链表项StateListEntr的 Next 和 Prev 指针的值都不为 0,说明这个线程还处于阻塞状态,并在某个同步对象的等待队列中;StartAddr域的值为IopConsoleDispatchThread,说明这个线程就是控制台派遣线程。

(3)关闭快速监视对话框,激活“调用堆栈”窗口。根据当前的调用堆栈,可以看到是由键盘中断服务程序(KdbIsr)进入的。在“调用堆栈”窗口中双击PspWakeThread函数对应的堆栈项。可以看到在此函数中连续调用了PspUnwaitThread函数和PspReadyThread函数,从而使处于阻塞状态的控制台派遣线程进入就绪状态。

(5)在“调用堆栈”窗口中双击PspUnwaitThread函数对应的堆栈项,按 F10 单步调试直到此函数的最后,然后再从快速监视对话框中观察“*Thread”表达式的值。

此时 State 域的值为 0(Zero) ,双向链表项 StateListEntry 的 Next 和 Prev 指针的值都为 0,说明这个线程已经处于游离状态,并已不在任何线程状态的队列中。

(6)按 F5 继续执行,在 PspReadyThread 函数中的断点处中断。按 F10 单步调试直到此函数的最后,然后再从快速监视对话框中观察“*Thread”表达式的值。

此时State 域的值为1(Ready), 双向链表项 StateListEntry的Next 和Prev指针的值都不为 0,说明这个线程已经处于就绪状态,并已经被放入优先级为24的就绪队列中。

 线程由运行状态进入就绪状态

(1)按 F5 继续执行,在 PspSelectNextThread 函数中的断点处中断。在快速监视对话框中查看“*PspCurrentThread”表达式的值。

其中State域的值为2(Running),双向链表项StateListEntry的 Next和Prev指针的值都为0,说明这个线程仍然处于运行状态,由于只能有一个处于运行状态的线程,所以这个线程不在任何线程状态的队列中;StartAddr 域的值为 LoopThreadFunction,说明这个线程就是 loop 线程。

(2)按 F10 单步调试,直到对当前线程的操作完成(也就是花括号中的操作完成)。再从快速监视对话框中查看“*PspCurrentThread”表达式的值。其中 State 域的值为 1(Ready),双向链表项StateListEntry 的 Next 和 Prev 指针的值都不为 0,说明 loop 线程已经进入了就绪状态,并已经被放入优先级为8的就绪队列中。

 线程由就绪状态进入运行状态

(1)按F5继续执行,在PspUnreadyThread函数中的断点处中断。在快速监视对话框中查看“*Thread”表达式的值。

其中State域的值为 1(Ready),双向链表项StateListEntry的Next 和Prev指针的值都不为0,说明这个线程处于就绪状态,并在优先级为24 的就绪队列中;StartAddr域的值为IopConsoleDispatchThread,说明这个线程就是控制台派遣线程。

(2)在“调用堆栈”窗口中激活 PspSelectNextThread 函数对应的堆栈项,在“调用堆栈”窗口中激活 PspUnreadyThread 函数对应的堆栈项,然后按 F10 单步调试,直到返回

PspSelectNextThread 函数并将线程状态修改为 Running。再从快速监视对话框中查看“*PspCurrentThread”表达式的值,观察当前占用处理器的线程的情况。

其中State域的值为2(Running),双向链表项StateListEntry的Next和Prev指针的值都为0,说明控制台派遣线程已经处于运行状态了。接下来,会将该线程的上下文从线程控制块(TCB)复制到处理器的各个寄存器中,处理器就可以从该线程上次停止运行的位置继续运行了。

 线程由运行状态进入阻塞状态

(1)按F5继续执行,在PspWait函数中的断点处中断。 在快速监视对话框中查看“*PspCurrentThread表达式的值。

State 域的值为 2(Running),双向链表项 StateListEntry的 Next和 Prev指针的值都为0,说明这个线程仍然处于运行状态;StartAdd域的值为 IopConsoleDispatchThread,说明这个线程就是控制台派遣线程。

(2)按 F10 单步调试,直到左侧的黄色箭头指向代码第 248行。再从快速监视对话框中查看“*PspCurrentThread”表达式的值。

其中 State 域的值为 3(Waiting),双向链表项StateListEntry 的 Next和Prev指针的值都不为 0,说明控制台派遣线程已经处于阻塞状态,并在某个同步对象的等待队列中。第 248行代码可以触发线程调度功能,会中断执行当前已经处于阻塞状态的控制台派遣线程,并将处理器上下文保存到该线程的线程控制块中。

3. 为线程增加挂起状态:

 要求:EOS 已经实现了一个resume命令,其命令函数为ConsoleCmdResumeThread

在这个命令中调用了 Resume 原语。Resume 原语可以将一个被 Suspend 原语挂起的线程(处于静止就绪状态)恢复为就绪状态。但是PsResumThread函数中的这部分代码(第 119行)还没有实现,要求在这个练习中完成这部分代码。

4.3思考与练习

在实验4中,当loop线程在控制台1中执行,并且在控制台2中执行suspend命令时,为什么控制台1中的loop线程处于就绪状态而不是运行状态?

答:当在控制台2中执行suspend命令时,控制台派遣线程被唤醒,由阻塞状态进入就绪状态。因为其优先级(24)比当前处于运行状态的loop线程的优先级要高,根据EOS已实现的基于优先级的抢先式调度算法,loop线程会进入就绪状态,控制台派遣线程会抢占处理器从而进入运行状态。 实质上是优先级为24的控制台派遣线程抢占了处理器,也就是控制台派遣线程处于运行状态,所以此时loop线程就处于就绪状态了。

实验5:进程的同步

5.1实验目的:

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

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

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

5.2实验内容:

1. 准备实验:启动OS Lab,新建一个EOS Kenel项目,新建一个EOS应用程序项目

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

使用 pc.c文件中的源代码,替换之前创建的EOS 应用程序项目中EOSApp.c文件内的源代码;按 F7生成修改后的EOS应用程序项目;按F5启动调试。在调试异常对话框中选择“否”,继续执行。立即激活虚拟机窗口查看生产者-消费者同步执行的过程,待应用程序执行完毕后,结束此次调试。

3. 调试EOS 信号量的工作过程:

 创建信号量

(1)按 F5启动调试EOS应用项目;在第77行添加一个断点。按 F5继续调试,到此断点处中断。按F11调试进入CreateSemaphore 函数;按 F11调试进入 semaphore.c 文件中的 PsCreateSemaphoreObject 函数。在 semaphore.c文件PsInitializeSemaphore函数第一行代码处添加一个断点。

(2)按 F5继续调试,到断点处中断。观察PsInitializeSemaphore函数中用来初始化信号

量结构体成员的值,应该和传入CreateSemaphore函数的参数值是一致的。按 F10 单步调试 PsInitializeSemaphore 函数执行的过程,查看信号量结构体被初始化的过程。

(3)打开“调用堆栈”窗口,查看函数的调用层次。

 等待、释放信号量

(1) 等待信号量(不阻塞):删除所有的断点,在 eosapp.c文件的Producer函数中,第144行添加一个断点;按 F5继续调试,到断点处中断。在 semaphore.c文第68行添加一个断点;按F5继续调试,到断点处中断。按 F10 单步调试,直到完成 PsWaitForSemaphore 函数中的所有操作。可以看到此次执行并没有进行等待,只是将Empty信号量的计数减少了1(由10变为了 9)就返回了。

(2) 释放信号量(不唤醒)

(3) 等待信号量(阻塞)

结束之前的调试。删除所有的断点。按 F5重新启动调试。在调试异常对话框中选择“是”,调试会中断。在 semaphore.c文件中第78行添加一个断点。按 F5继续调试,并立即激活虚拟机窗口查看输出。

查看“调用堆栈”窗口,在“调用堆栈”窗口中双击 Producer 函数所在的堆栈帧,绿色

箭头指向等待 Empty 信号量的代码行,查看Producer函数中变量i 的值为14,表示生产者线程正在尝试生产14 号产品。

在“调用堆栈”窗口中双击 PsWaitForSemaphore 函数的堆栈帧,查看 Empty 信号量计数(Semaphore->Count)的值的值为-1

(4)释放信号量(唤醒):

删除所有断点。在 eosapp.c文件第180行添加一个断按 F5继续调试,到断点处中断;使用F10和 F11 调试进入PsReleaseSemaphore函数。按 F10单步调试PsReleaseSemaphore函数,直到在第132行处中断。在 semaphore.c文件第 83行添加一个断点。按 F5继续调试,在断点处中断。 查看PsWaitForSemaphore函数中 Empty 信号量计数的值为0 ,激活 Producer函数的堆栈帧,查看Producer函数中变量i的值为 14,表明之前被阻塞的、正在尝试生产14号产品的生产

者线程已返回并执行了。

5.3思考与练习

实验5中,生产者线程和消费者线程是如何使用Mutex、Empty信号量和Full信号量来实现同步的?这两个线程函数中对这三个同步对象的操作能够改变顺序吗?

答:Mutex对象只是用来保护临界资源的,防止生产者线程和消费者线程同时访问临界资源,从而造成混乱。Empty信号量和Full信号量是相互合作不可分割的,在生产者线程中,Empty信号量保证在没有空缓冲区的时候让生产者停止生产,Full信号量保证在生产完毕一个产品后增加一个满缓冲区;在消费者线程中,Full信号量保证在没有满缓冲区的时候让消费者停止消费,Empty信号量保证在消费完毕一个产品后增加一个空缓冲区。所以对这三个同步对象的操作是不能改变顺序的。

实验6:时间片轮转调度

6.1实验目的:

1. 调试 EOS 的线程调度程序,熟悉基于优先级的抢先式调度。

2. 为 EOS添加时间片轮转调度,了解其它常用的调度算法。

6.2实验内容:

1.准备实验:启动OS Lab,新建一个EOS Kenel项目

2.阅读控制台命令“rr”相关的源代码

 按F7生成在本实验3.1中创建的EOS Kernel项目。按F5启动调试。待 EOS启动完毕,在EOS控制台中输入命令“rr”后按回车。

3.调试线程调度程序:

 调试当前线程不被抢先的情况

(1)结束之前的调试,在680行添加一个断点。按F5启动调试。待EOS启动完毕,在EOS控制台中输入命令“rr”后按回车。查看ThreadFunction 函数中变量

pThreadParameter->Y 的值应该为 0,说明正在调试的是第 0个新建的线程。

激活虚拟机窗口,激活OS Lab窗口后按 F5使第 0个新建的线程继续执行,再次激活虚拟机窗口,第 0 个新建的线程已经在控制台中输出了第一轮循环的内容。可以多按几次 F5 查看每轮循环输出的内容。

(2)在第384行添加一个断点。按F5继续执行。激活“监视”窗口,在“监视”窗口中添加表达式“/t PspReadyBitmap”,以二进制格式查看就绪位图的值。此时就绪位图的值应该为100000001,表示优先级为8和 0的两个就绪队列中存在就绪线程。

在“快速监视”对话框的“表达式”中输入表达式“*PspCurrentThread”后,点击“重新计算”按钮。在“监视”窗口中添加表达式“ListGetCount(&PspReadyListHeads[8])”,查看优先级为8的就绪队列中就绪线程的数量,值为19。说明除了正在执行的第 0 个新建的线程外,其余 19 个新建的线程都在优先级为8 的就绪队列中。按 F10 单步调试,查看变量HighestPriority的值为8。继续按 F10 单步调试,直到在

PspSelectNextThread 函数返前停止,注意观察线程调度执行的每一个步骤:

 调试当前线程被抢先的情况

(1)删除之前添加的所有断点。在第395行添加一个断点。按 F5继续执行,激活虚拟机窗口。

(2)在虚拟机窗口中按下一次空格键。在“监视”窗口中查看就绪位图的值为

1,说明此时在优先级为 24的就绪队列中存在就绪线程。

在“监视”窗口中添加表达式“ListGetCount(&PspReadyListHeads[24])”,其值为 1。按F10单步调试一次,执行的语句会将当前正在执行的第0 个新建的线程,放入优先级为8的就绪队列的队首。

(3)继续按 F10 单步调试,直到在第 444 行中断执行,注意观察线程调度执行的每一个步骤。按 F10单步调试一次,当前线程PspCurrentThread指向了优先级为24的线程。在“快速监视”窗口中查看表达式“*PspCurrentThread”的值。继续按 F10单步调试,直到在PspSelectNextThread函数返回前(第465 行)中断执行,注意观察线程调度执行的每一个步骤。在“监视”窗口中,就绪位图的值变为100000001,优先级为24 的就绪队列中线程的数量变为0,删除所有断点后结束调试。

4. 为EOS 添加时间片轮转调度

 要求:修改 ps/sched.c文件中的PspRoundRobin函数(第 337行),在其中实现时间片轮转调度算法。代码修改完毕后,按F7生成 EOS内核项目按 F5启动调试。在EOS控制台中输入命令“rr”后按回车。

5. 修改线程时间片的大小

 在 OS Lab的“项目管理器”窗口中找到ps/psp.h文件,双击打开此文件。将ps/psp.h第104行定义的TICKS_OF_TIME_SLICE的值修改为1。按F7生成EOS内核项目。按 F5启动调试。在EOS控制台中输入命令“rr”后按回车。观察执行的效果。

6.3思考与练习:

在实验6中,结合线程调度执行的时机,说明在ThreadFunction函数中,为什么可以使用“关中断”和“开中断”的方法来保护控制台这种临界资源。一般情况下,应该使用互斥信号量(MUTEX)来保护临界资源,但是在ThreadFunction函数中却不能使用互斥信号量,而只能使用“关中断”和“开中断”的方法,结合线程调度的对象说明这样做的原因。

答:关中断后CPU就不会响应任何由外部设备发出的硬中断(包括定时计数器中断和键盘中断等)了,也就不会发生线程调度了,从而保证各个线程可以互斥的访问控制台。 这里绝对不能使用互斥信号量(MUTEX)保护临界资源的原因:如果使用互斥信号量,则那些由于访问临界区而被阻塞的线程,就会被放入互斥信号量的等待队列,就不会在相应优先级的就绪队列中了,而时间片轮转调度算法是对就绪队列的线程进行轮转调度,而不是对这些被阻塞的线程进行调度,也就无法进行实验了。使用“关中断”和“开中断”进行同步就不会改变线程的状态,可以保证那些没有获得处理器的线程都在处于就绪队列中。

附录:

if(NULL!=PspCurrentThread&&Running==PspCurrentThread->State)

{ PspCurrentThread->RemainderTicks--;

if (0 == PspCurrentThread->RemainderTicks) {

PspCurrentThread->RemainderTicks = TICKS_OF_TIME_SLICE;

if(BIT_TEST(PspReadyBitmap, PspCurrentThread->Priority))

{ PspReadyThread(PspCurrentThread); } }

实验7:物理存储器与进程逻辑地址空间的管理

7.1实验目的:

1. 通过查看物理存储器的使用情况,并练习分配和回收物理内存,从而掌握物理存储器的管理方法。

2. 通过查看进程逻辑地址空间的使用情况,并练习分配和回收虚拟内存,从而掌握进程逻辑地址空间的管理方法。

7.2实验内容:

1. 准备实验:启动OS Lab,新建一个EOS Kenel项目

2. 阅读控制台命令“pm”相关的源代码,并查看其执行的结果

 按 F7生成在本实验3.1中创建的EOS Kernel项目。按F5启动调试。待 EOS启动完毕,在EOS控制台中输入命令“pm”后按回车

3. 分配物理页和释放物理页

 使用 pm.c 文件中 ConsoleCmdPhysicalMemory 函数的函数体替换 ke/sysproc.c

文件中ConsoleCmdPhysicalMemory函数的函数体。按F7生成修改后的EOS Kernel项目。按 F5启动调试。待 EOS启动完毕,在EOS控制台中输入“pm”后回车。

 在ke/sysproc.c文件的第1103行添加一个断点,在第1115行添加一个断点。按F5启动调试。待 EOS启动完毕,在EOS控制台中输入命令“pm”后按回车。pm命令开始执行后,会在调用MiAllocateAnyPages函数的代码行处中断,按F11 调试进入MiAllocateAnyPages函数。

 按 F5继续执行,在调用MiFreePages函数代码行处中断,按F11调试进入MiFreePages函数。按 F10单步调试MiFreePages函数执行过程

4. 阅读控制台命令“vm”相关的源代码,并查看其执行的结果

 按 F5启动调试。待 EOS 启动完毕,在 EOS 控制台中输入命令“pt”后按回车。在EOS控制台中输入命令“vm 1”后按回车。

将本实验文件夹中的 文件添加到软盘镜像的根目录中,按F5启动调试,待 EOS启动完毕,在EOS控制台中输入命令“A:”后按回车:

此时按 Ctrl+F2 切换到“Console-2” ,然后输入命令“pt”后按回车,输入命令“vm 1”后按回车;输入命令“vm 31”后按回车。

5. 在系统进程中分配虚拟页和释放虚拟页

 使用 vm.c文件中 ConsoleCmdVM函数的函数体替换ke/sysproc.c文件中ConsoleCmdVM函数的函数体,按 F7生成修改后的EOS Kernel项目按 F5启动调试。待 EOS启动完毕,在EOS控制台中输入命令“vm 1”后按回车;

 在第1082行、在第1147行添加断点。按 F5启动调试。待EOS启动完毕,在EOS控制台中输入“vm 1”后回车。按F11 调试进入MmAllocateVirtualMemory函数。

 按 F5继续执行,会在调用MmFreeVirtualMemory函数的代码行处中断。此时要注意参数BaseAddress和RegionSize初始化的值。按F11 调试进入MmFreeVirtualMemory函数。

 结束此次调试后,继续按照下列要求修改 ConsoleCmdVM函数的源代码,加深对虚拟页分配和释放过程的理解:

(1)尝试在调用MmAllocateVirtualMemory函数时将RegionSize参数的值设置为PAGE_SIZE+1或者PAGE_SIZE*2+1。观察“输出”窗口中转储的信息,并说明申请虚拟内存的大小与实际分配的大小之间的关系,以及分配的虚拟内存大小会对分配的虚拟地址产生什么样的影响。将“输出”窗口中转储的信息保存在文本文件中。 如下左图:

(2)尝试在调用MmAllocateVirtualMemory

函数时将BaseAddress参数的值设置为已经

被占用的虚拟内存,例如0xA0000000,观察“输出”窗口中转储的信息。将“输出”窗口中转储的信息保存在文本文件中。如上右图:

(3)尝试在调用MmAllocateVirtualMemory函数时将RegionSize参数的值设置为PAGE_SIZE*2,将BaseAddress参数的值设置为0xA0017004,将“输出”窗口中转储的信息保存在文本文件中。

6. 在应用程序进程中分配虚拟页和释放虚拟页

 要求:创建一个EOS 应用程序,并编写代码完成下列功能:

1. 调用 API 函数 VirtualAlloc,分配一个整型变量所需的空间,并使用一个整型变量的指针指向这个空间。修改整型变量的值为0xFFFFFFFF。在修改前输出整型变量的值,在修改后再输出整型变量的值。

2. 调用API函数 Sleep,等待 10秒钟。

3. 调用 API 函数 VirtualFree,释放之前分配的整型变量的空间,进入死循环,这样应用程序就不会结束。

实验8:分页存储器管理

8.1实验目的:

1. 学习 i386处理器的二级页表硬件机制,理解分页存储器管理原理;

2. 查看 EOS应用程序和系统进程的二级页表映射信息,理解页目录和页表管理方式;

3. 编程修改页目录和页表的映射关系,理解分页地址变换原理。

8.2实验内容:

1. 准备实验:启动OS Lab,新建一个EOS应用程序项目

2. 查看 EOS 应用程序进程的页目录和页表

使用 memory.c文件中的源代码替换之前创建的 EOS 应用程序项目中 EOSApp.c 文件中的源代码。点击“添加”按钮添加并自动打开文件。将 文件中的源代码复制到文件中。按F7生成修改后的EOS应用程序项目。按 F5启动调试。将“输出”窗口中的内容复制到一个文本文件中。

3. 查看应用程序进程和系统进程并发时的页目录和页表

结束之前的调试。取消 EOSApp.c第121 行语句的注释。按F7生成修改后的EOS应用程序项目。按 F5启动调试。在“Console-1”中会自动执行 ,创建该应用程序进程。利用其等待 10 秒的时间,按Ctrl+F2切换到“Console-2”。在“Console-2”中输入命令“mm”后按回车,会将系统进程的二级页表映射信息输出到虚拟机窗口和 OS Lab 的“输出”窗口,将“输出”窗口中的内容复制到一个文本文件中。

4. 查看应用程序进程并发时的页目录和页表

结束之前的调试。取消 EOSApp.c第201 行语句的注释。按F7生成修改后的EOS应用程序项目。按 F5启动调试。在“Console-1”中会自动执行,创建该应用程序进程。利用其等待10秒的时间,按Ctrl+F2 切换到“Console-2”。在“Console-2”中输入“eosapp”后按回车,再执行一个。由

创建的两个并发进程会先后在各自的控制台和 OS Lab“输出”窗口中,输出各自的二级页表映射信息将“输出”窗口中的内容

复制到一个文本文件中。

5. 在二级页表中映射新申请的物理页

(1)新建一个 EOS Kernel项目。打开本实验文件夹中的MapNewPage.c文件,在sysproc.c

文件的ConsoleCmdMemoryMap 函数中找到“关中断”的代码行,将MapNewPage.c文件中的代码插入到“关中断”代码行之后。按 F7生成该内核项目,按 F5启动调试。在 EOS控制台中输入命令“mm”后回车。结束此次调试,删除或者注释会触发异常的该行代码。

(2)按F7生成该内核项目,按 F5启动调试。在 EOS控制台中输入“mm”后回车。在OS Lab 的“输出”窗口中查看执行结果,将“输出”窗口中的内容复制到一个文本文件中。

8.3思考与练习:

实验8中,图16-2(a)中应用程序进程的页目录和页表占用了几个物理页?页框分别是多少?图16-2(a)中应用程序进程映射用户地址空间(低2G)的页表的页号是多少?

答:页目录占用一个物理页,页框号是0x409。页表占用5个物理页(不算复用为页表的页目录),页框号是0x41D、0x401、0x403、0x404、0x402。映射用户地址空间的页表的页框号是0x41D。该页表有11个有效的PTE,页框号是0x41E,0x41F,0x420,0x421,0x422,0x423,0x424,0x425,0x426,0x427,0x428。

实验10:磁盘调度算法

10.1实验目的:

1. 通过学习EOS 实现磁盘调度算法的机制,掌握磁盘调度算法执行的条件和时机。

2. 观察 EOS 实现的FCFS、SSTF和 SCAN磁盘调度算法,了解常用的磁盘调度算法。

3. 编写 CSCAN和 N-Step-SCAN磁盘调度算法,加深对各种扫描算法的理解。

10.2实验内容:

1.准备实验:启动OS Lab,新建一个EOS Kenel项目

2. 验证先来先服务(FCFS)磁盘调度算法

 在“项目管理器”窗口中双击ke文件夹中的sysproc.c文件,打开此文件。在 sysproc.c文件的第580行找到控制台命令“ds”对应的函数ConsoleCmdDiskSchedule。打开io/block.c文件,在第 378行找到磁盘调度算法函数IopDiskSchedule。按 F7生成项目,然后按F5 启动调试。待 EOS启动完毕,在EOS控制台中输入命令“ds”后按回车。

结论:对比EOS控制台和“输出”窗口中的内容,可以发现FCFS算法是根据线程访问磁盘的先后顺序进行调度的。

3. 验证最短寻道时间优先(SSTF)磁盘调度算法

 使用 sstf.c 文件中 IopDiskSchedule 函数的函数体,替换 block.c 文件中

IopDiskSchedule 函数的函数体。按 F7生成项目,然后按F5 启动调试。待 EOS启动完毕,在EOS控制台中输入命令“ds”后按回车。

4. 验证SSTF算法造成的线程“饥饿”现象

 修改sysproc.c文件ConsoleCmdDiskSchedule函数中的源代码,仍然使磁头初始停留在磁道10,而让其它线程依次访问磁道78、21、9、8、11、41、10、67、12、10。按 F7生成项目,然后按F5 启动调试。待 EOS启动完毕,在EOS控制台中输入命令“ds”后按回车。

结论:可以发现,虽然访问 78 号磁道的线程的请求第一个被放入请求队列,但却被推迟到最后才被处理,出现了“饥饿”现象

5. 验证扫描(SCAN)磁盘调度算法

 使用 scan.c 文件中IopDiskSchedule 函数的函数体,替换 block.c 文件中

IopDiskSchedule 函数的函数体。按F7生成项目,然后按F5启动调试。待EOS启动完毕,在EOS控制台中输入命令“ds”后按回车。

结论:对比SCAN算法与SSTF算法在“输出”窗口中的内容,可以看出,SCAN算法的平均寻道数有可能小于SSTF 算法,所以说SSTF算法不能保证平均寻道数最少。

 使用 SCAN算法调度在本实验3.4中产生“饥饿”现象的数据,验证SCAN 算法能够解决“饥饿”现象

6. 改写SCAN算法

 要求:在已有 SCAN 算法源代码的基础上进行改写,要求不再使用双重循环,而是只遍历一次请求队列中的请求,就可以选中下一个要处理的请求。

 测试:使用本实验 3.2 中的数据进行测试

7. 编写循环扫描(CSCAN)磁盘调度算法

 要求:在已完成的SCAN算法源代码基础上进行改写,不再使用全局变量ScanInside确定磁头移动方向,规定磁头只能从外向内移动。当磁头移动到最内被访问磁道时,磁头立即移动到最外被访问磁道,即将最大磁道号紧接着最小磁道号构成循环进行扫描。

 测试:使用本实验 3.2 中的数据进行测试

8. 验证SSTF、SCAN 及 CSCAN算法中的“磁臂粘着”现象

修改sysproc.c文件ConsoleCmdDiskSchedule函数中的源代码,仍然使磁头初始停留在磁道10而让其它线程依次访问磁道78、10、10、10、10、10、10、10、10、10。分别使用SSTF、SCAN 和CSCAN算法调度这组数据。

 SSTF算法

 SCAN算法

 CSCAN算法

9. 编写N-Step-SCAN磁盘调度算法

 要求:在已经完成的 SCAN 算法源代码的基础上进行改写,将请求队列分成若干个长

度为 N 的子队列,调度程序按照 FCFS原则依次处理这些子队列,而每处理一个子队列时,又是按照SCAN算法。

 测试:使用本实验 3.2 中的数据进行测试,将宏定义 SUB_QUEUE_LENGTH 的值修改为 100,算法性能接近于SCAN 算法的性能

 测试:使用本实验3.2中的数据,将宏定义 SUB_QUEUE_LENGTH 的值修改为1

10.3部分代码

如N-STEP-SCAN算法:

IopDiskSchedule(VOID)

{ PLIST_ENTRY pListEntry;

PREQUEST pRequest;

PREQUEST INpNextRequest = NULL;

PREQUEST OUTpNextRequest = NULL;

LONG Offset;

pNextRequest = pRequest;

goto

RETURN; }

else if( Offset > 0 && Offset <

InsideShortestDistance ) {

InsideShortestDistance = Offset;

INpNextRequest = pRequest; }

else if( Offset < 0 && -Offset >

OutsideShortestDistance ) {

OutsideShortestDistance = -Offset;

OUTpNextRequest = pRequest; }}

if( INpNextRequest ) {

return INpNextRequest; } else {

return OUTpNextRequest; }

RETURN:

return pNextRequest; }

ULONG InsideShortestDistance = 0xFFFFFFFF;

ULONG OutsideShortestDistance = 0x00000000;

PREQUEST pNextRequest = NULL;

for(pListEntry = ;

pListEntry != &RequestListHead;

pListEntry = pListEntry->Next) {

pRequest = CONTAINING_RECORD(pListEntry,

REQUEST, ListEntry);

Offset = pRequest->Cylinder - CurrentCylinder;

if(0 == Offset) {

三、实验心得与体会

1、做实验时,最开始并不是很了解OS Lab平台的使用,即使对着老师给的实验教程做还是不怎么会,于是请教会做的同学,通过同学的讲解我知道了怎样在OS Lab平台上建立项目,怎样更改路径并找到项目的源文件等等基本操作。

掌握对平台的简单应用后,做后面的实验我是按照实验教程上的步骤一步步的实施,并且每次都认真观察相应的运行结果,每个实验都会建议我们学习实验教程前面的理论部分,我想如果对他的理论不熟悉,就算试验成功了我也不知道为什么,所以我一般在做实验前会对前面的理论部分进行简要的学习和熟悉。做实验的过程中,有时候按照实验教程上的步骤做平台还是会出现一些错误,比如做实验四按空格键的时候,总是得不到想要的结果,最后问其他同学才发现自己理解错误。

之后做试验是遇到问题我还是选择多问同学,毕竟每个人擅长的是不同的,有些问题这个同学会解决,有些问题则是那个同学才懂解决,通过互相交流和学习,我们通过实验不仅巩固了课堂上学到的相关知识,也对操作系统有了更深的了解。

2、其实做完实验我还是不能保证我对OS Lab这个平台有很好的全面的了解,但是对一些基本操作及其快捷键我算是大致掌握了,通过这个平台我也是认识到了“没有做不到的,只有想不到的”,我觉得创建这个平台的人们真的是很了不起,这个平台让我们便动手便了解了平时我们看不到的操作系统的相关知识。要做好实验,得按照实验教程上面的内容一步步落实,要边做变领悟相关原理及运行结果的出现的原因,这样我们才能在试验中学到更多、掌握更多。其次,也遇到问题我们自然是要先自己思考,通过不同的尝试来解决,之后不能解决的我们要多向老师同学请教,通过互相交流得来的知识也是会让我们难忘的。

四、参考文献

[1] 海西慧学,EOS操作系统实验教程. 北京海西慧学科技有限公司

[2] 计算机操作系统,西安电子科技大学出版社


本文标签: 线程 调试 函数 实验 进程