合肥工业大学操作系统实验报告

合肥工业大学

计算机与信息学院

实验报告

课 程: 计算机操作系统

专业班级: 计算机科学与技术2班

学 号:

姓 名:

实验1 实验环境的使用

一. 实验目的

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

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

二. 实验内容

1.启动OS Lab

2. 学习OS Lab的基本使用方法

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

2.2生成项目

2.3执行项目

2.4调试项目

2.4.1 使用断点中断执行

2.4.2单步调试

2.4.3查看变量的值

2.4.4调用堆栈

3. EOS内核项目的生成和调试

3.1新建EOS 内核项目

3.2生成项目

3.3调试项目

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

3.5查看EOS SDK(Software Development Kit)文件夹

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

4.1新建EOS 应用程序项目

4.2生成项目

4.3调试项目

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

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

5 退出OS Lab

6 保存EOS 内核项目

三. 实验结果

本实验主要是熟悉EOS 操作系统的基本操作,练习了:

(1)新Windows 控制台应用程序项,

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

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

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

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

新建完毕后, OS Lab 会自动打开这个新建的项目。

(2)在“生成”菜单中选择“生成项目”。

结果如图

(3)执行项目:选择“调试”菜单中的“开始执行”

(4)调试项目:

1. 右键点击“项目管理器”窗口中的“源文件”文件夹节点,在弹出的快捷菜单中选择“添加”中的“添加新文件”。

2. 在弹出的“添加新文件”对话框中选择“C 源文件”模板。

3. 在“名称”中输入文件名称“func ”。

4. 点击“添加”按钮,添加并自动打开文件func.c ,此时的“项目管理器”窗口会如图:

(5). 在 func.c 文件中添加函数:

int Func (int n) { n = n + 1;

return n; }

(6). 点击源代码编辑器上方的console.c 标签,切换到console.c 文件。将 main 函数修改为:

int main (int argc, char* argv[])

{ int Func (int n); // 声明Func 函数

int n = 0; n = Func(10);

printf ("Hello World!\n");

return 0; }

代码修改完毕后按F7

实验结果为输出:Hello World!

(7). 在main 函数中定义变量n 的代码行

int n = 0; 上点击鼠标右键,在弹出的快捷菜单中选择“插入/删除断点”,会在此行左侧的空

白处显示一个红色

圆点,表示已经成功

在此行代码添加了一个断点

练习使用“逐过程”, “逐语句”,“跳出”功能

(8). 在源代码编辑器中变量n 的名称上点击鼠标右键,在弹出的快捷菜单中选择“快速监视”,进行单步测试,观察n 结果依次为0,11

(9)调用堆栈,选择“调试”菜单“窗口”中的“调用堆栈”,激活“调用堆栈”窗口。可以看到当前“调用堆栈”窗口中只有一个main 函数(显示的内容还包括了参数值和函数地址)。

按F11(“逐语句”功能的快捷键)调试,直到进入Func 函数 ,其中当前正在调试的Func 函数在栈顶位置,main 函数在栈底位置。说明是在main 函数中调用了Func 函数。

(10)查看软盘镜像文件中的内容,在“项目管理器”窗口中双击软盘镜像文件Floppy.img

四. 实验总结

今天第一次进行操作系统这门课的实验,这也是将抽象的理论知识应用到实践的一个很好的机会,同时,我们也学习了使用OS Lab 的这个实验环境,OS Lab 的操作界面和Microsoft Visual Studio 2010很像,所以使用起来很快就能上

手,对于实验内容,今天主要练习了:新建Windows 控制台应用程序项目、生成项目、执行项目、调试项目以及EOS 应用程序项目的生成和调试;设置间断点,并且在它的基础上进行了单步操作;详细观察了通过“快速监视“标记的数的值的变化过程等基本操作,这也为以后实验的顺利进行打下了良好的基础。

五. 实验截图

实验2 操作系统的启动

一. 实验目的

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

2. 查看EOS 启动后的状态和行为,理解操作系统启动后的工作方式。 二. 实验内容 1 准备实验

2 调试EOS 操作系统的启动过程 2.1 使用Bochs 做为远程目标机 2.2 调试BIOS 程序 2.3 调试软盘引导扇区程序 2.4 调试加载程序 2.5 调试内核

2.6 EOS启动后的状态和行为

三. 实验结果

1. 新建一个EOS Kernel项目。 2. 使用Bochs 做为远程目标机

(1)在“项目管理器”窗口中,右键点击项目节点,在弹出的快捷菜单中选择“属性”。

(2) 在弹出的“属性页”对话框右侧的属性列表中找到“远程目标机”属性,将此属性值修改为“Bochs Debug”

(3)点击“确定”按钮关闭“属性页”对话框。接下来就可以使用Bochs 模拟器调试BIOS 程序和软盘引导扇区程序了。

3. 按F5启动调试,此时会弹出两个Bochs 窗口。标题为“Bochs for windows - Display ”的窗口相当于计算机的显示器,显示操作系统的输出。标题为“Bochs for windows - Console”的窗口是Bochs 的控制台,用来输入调试命令,输出各种调试信息。

4. 启动调试后,Bochs 在CPU 要执行的第一条指令(即BIOS 的第一条指令)处中断。 此时,Display 窗口没有显示任何内容,Console 窗口显示要执行的BIOS 第一条指令的相关信息,并等待用户输入调试命令

5. 然后查看CPU 在没有执行任何指令之前主要寄存器中的数据,以及内存中的数据

(1)在Console 窗口中输入调试命令sreg 后按回车,显示当前CPU 中各个段寄存器的值

(2)输入调试命令r 后按回车,显示当前CPU 中各个通用寄存器的值 (3)输入调试命令xp /1024b 0x0000,查看开始的1024个字节的物理内存。在Console 中输出的这1K 物理内存的值都为0,说明BIOS 中断向量表还没有被加载到此处。

(4)输入调试命令xp /512b 0x7c00,查看软盘引导扇区应该被加载到的内存位置。输出的内存值都为0,说明软盘引导扇区还没有被加载到此处。

6.EOS 启动后的状态和行为

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

结果如图

(2)查看EOS 启动后的进程和线程的信息: 在控制台中输入命令“pt ”后按回车。 输出的进程和线程信息如图所示

四. 实验总结

今天的实验在实验一的基础上学习了EOS 操作系统的启动、跟踪调试以及EOS 在PC 机上从加电复位到成功启动的全过程,这让我们对EOS 这个平台有了初步的掌握,同时我们也练习了查看了EOS 启动后的状态和行为,学会了查看CPU 主要寄存器中以及内存中的数据的方法。 五. 实验截图

实验3进程的创建

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

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

二,预备知识:

阅读本书第 5.1 节,重点理解程序和进程的关系,熟悉进程控制块结构体以及进程创建的过程。仔细

学习CreateProcess 函数和其它与创建进程相关的函数的说明, 注意理解这些函数的参数和返回值的意义。

三,主要实验内容及步骤: 1, 准备实验;

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

练习使用控制台命令创建 EOS 应用程序进程的具体步骤如下:

(1) 在 EOS 应用程序项目的“项目管理器”窗口中双击 Floppy.img 文件,使用 FloppyImageEditor 工具打开此软盘镜像文件。

(2)将本实验文件夹中的 Hello.exe 文件拖动到 FloppyImageEditor 工具窗口的文件列表中释放,

Hello.exe 文件即被添加到软盘镜像文件中。Hello.exe 一个 EOS 应用程序,其源代码可以参见

本实验文件夹中的 Hello.c 源文件。

(3)在 FloppyImageEditor 中选择“文件”菜单中的“保存”后关闭 FloppyImageEditor 。

(4)按 F7 生成 EOS 应用项目。 (5)按 F5 启动调试。OS Lab 会弹出一个调试异常对话框,并中断应用程序的执行。

(6) 在调试异常对话框中选择“否”,忽略异常继续执行应用程序。

(7) 激活虚拟机窗口,待该应用程序执行完毕后,在 EOS 的控制台中输入命令“A:\Hello.exe”后回 车。

(8) Hello.exe 应用程序开始执行,观察其输出如图 11-1。

(9)待 Hello.exe 执行完毕后可以重复第 7 步,或者结束此次调试。

3. 练习通过编程的方式让应用程序创建另一个应用程序的进程 使用 OS Lab 打开本实验文件夹中的 NewProc.c 文件(将此文件拖动到 OS Lab 窗口中释放即可)

按照下面的步骤查看应用程序创建另一个应用程序的进程的执行结果:

(1) 使用NewProc.c 文件中的源代码替换之前创建的EOS 应用程序项目中的EOSApp.c 文件内的源代码。

(2)按 F7 生成修改后的 EOS 应用程序项目。

(3) 按 F5 启动调试。OS Lab 会首先弹出一个调试异常对话框。 (4) 在调试异常对话框中选择“否”,继续执行。 (5)激活虚拟机窗口查看应用程序输出的内容, 如图 11-2。 结合图 11-1, 可以看到父进程 (EOSApp.exe )

首先开始执行并输出内容,父进程创建了子进程(Hello.exe )后,子进程开始执行并输出内容,

待子进程结束后父进程再继续执行。 (6)结束此次调试。

4. 调试 CreateProcess 函数

按照下面的步骤调试 CreateProcess 函数创建进程的过程: (1) 按 F5 启动调试 EOS 应用程序,OS Lab 会首先弹出一个调试异常对话框。 (2) 选择“是”调试异常,调试会中断。

(3)在 main 函数中调用 CreateProcess 函数的代码行(第 57 行)添加一个断点。

(4)按 F5 继续调试,在断点处中断。 (5)按 F11 调试进入 CreateProcess 函数。此时已经开始进入 EOS 内核进行调试。

.5, 调试PsCreateProcess 函数

创建进程最主要的操作就是创建进程控制块(PCB),并初始化其中的各种信息(也就是为进程分配各种资源) 。所以在 PsCreateProcess 函数中首先调

用了 PspCreateProcessEnvironment 函数来创建进程控制块。

调试 PspCreateProcessEnvironment 函数的步骤如下:

(1)在 PsCreateProcess 函数中找到调用 PspCreateProcessEnvironment 函数的代码行(create.c

文件的第 163 行) ,并在此行添加一个断点。

(2) 按 F5 继续调试,到此断点处中断。

(3) 按 F11 调试进入 PspCreateProcessEnvironment 函数。

由于 PspCreateProcessEnvironment 函数的主要功能是创建进程控制块并初始化其中的部分信息,所

以在此函数的开始,定义了一个进程控制块的指针变量 NewProcess。在此函数中查找到创建进程控制块的

代码行(create.c 文件的第 418 行)

Status = ObCreateObject( PspProcessType,

NULL,

sizeof(PROCESS) + ImageNameSize + CmdLineSize,

0,

(PVOID*)&NewProcess );

这里的 ObCreateObject 函数会在由 EOS 内核管理的内存中创建了一个新的进程控制块(也就是分配了一

块内存) ,并由 NewProcess 返回进程控制块的指针(也就是所分配内存的起始地址) 。

按照下面的步骤调试进程控制块的创建过程:

(1)在调用 ObCreateObject 函数的代码行(create.c 文件的第 418 行)添加一个断点。

(2)按 F5 继续调试,到此断点处中断。

(3) 按 F10 执行此函数后中断。

(4) 此时为了查看进程控制块中的信息,将表达式*NewProcess 添加到“监视”窗口中。

(5) 将鼠标移动到“监视”窗口中此表达式的“值”属性上,会弹出一个临时窗口,在临时窗口中会

按照进程控制块的结构显示各个成员变量的值(可以参考 PROCESS 结构体的定义) 。由于只是新

建了进程控制块,还没有初始化其中成员变量,所以值都为 0。

接下来调试初始化进程控制块中各个成员变量的过程:

(1)首先创建进程的地址空间,即 4G 虚拟地址空间。在代码行(create.c 文件的第 437 行)

NewProcess->Pas = MmCreateProcessAddressSpace();

添加一个断点。

(2) 按 F5 继续调试,到此断点处中断。

(3)按 F10 执行此行代码后中断。

(4)在“监视”窗口中查看进程控制块的成员变量 Pas 的值已经不再是 0。说明已经初始化了进程的

4G 虚拟地址空间。

(5)使用 F10 一步步调试 PspCreateProcessEnvironment 函数中后面的代码,

在调试的过程中根据执

行的源代码,查看“监视”窗口中*NewProcess 表达式的值,观察进程控制块中哪些成员变量是

被哪些代码初始化的,哪些成员变量还没有被初始化。

(6)当从 PspCreateProcessEnvironment 函数返回到 PsCreateProcess 函数后, 停止按 F10。 此时 “监

视”窗口中已经不能再显示表达式*NewProcess 的值了,在 PsCreateProcess 函数中是使用

ProcessObject 指针指向进程控制块的,所以将表达式*ProcessObject 添加到“监视”窗口中就

可以继续观察新建进程控制块中的信息。

(7)接下来继续使用 F10 一步步调试 PsCreateProcess 函数中的代码,同样要注意观察执行后的代码

修改了进程控制块中的哪些成员变量。当调试到 PsCreateProcess 函数的最后一行代码时,查看

进程控制块中的信息, 此时所有的成员变量都已经被初始化了 (注意观察成员 ImageName 的值) 。

(8)按 F5 继续执行,EOS 内核会为刚刚初始化完毕的进程控制块新建一个进程。激活虚拟机窗口查看

新建进程执行的结果。

(9)在 OS Lab 中选择“调试”菜单中的“停止调试”结束此次调试。

(10)选择“调试”菜单中的“删除所有断点” 。

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

四.实验收获及感悟:

通过本次实验熟悉了进程的创建,对进程更加的了解。也让我更加熟悉OS Lab 这个软件,对于EOS 操作系统的理解也更加深刻。

五.实验截图

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

一,实验目的:

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

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

二,预备知识:

阅读本书第 5.2.3 节,了解线程都有哪些状态以及 EOS 是如何定义这些状态的。了解线程是如何在这些状态之间进行转换的,特别是要阅读一下 EOS 中用于线程转换的相关函数的源代码。阅读本书第 5.2.4节, 了解 EOS 为线程添加的挂起状态, 以及 Suspend 和 Resume 原语操作。 线程状态的转换和线程的同步、线程的调度是不可分割的,所以建议读者简单学习一下第 5.3 和 5.4 节中的内容。

此外,由于本实验需要观察“控制台派遣线程”在不同状态间的转换过程,所以读者也需要对该线程有一个简单的了解。控制台派遣线程做为一个系统线程(优先级为 24) ,在 EOS 启动后就会被创建,但是

该线程绝大部分时间都处于阻塞状态,只有当发生键盘事件(例如键被按下)时才会被唤醒,当该线程将键盘事件派遣到当前活动的控制台后,该线程就会重新回到阻塞状态等待下一个键盘事件到来。该线程的线程函数是文件 io/console.c 中的 IopConsoleDispatchThread 函数(第 567 行) 。

三,主要实验内容及步骤:

1,准备实验;

2,调试线程状态的转换过程;

在本练习中,会在与线程状态转换相关的函数中添加若干个断点,并引导读者单步调试这些函数,使读者对 EOS 中的下列线程状态转换过程有一个全面的认识: 线程由阻塞状态进入就绪状态。

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

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

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

为了完成这个练习,EOS 准备了一个控制台命令“loop ” ,这个命令的命令函数是 ke/sysproc.c 文件中的 ConsoleCmdLoop 函数(第 797 行) ,在此函数中使用 LoopThreadFunction 函数(第 755 行)创建了一个优先级为 8 的线程(后面简称为“loop 线程” ) ,该线程会在控制台中不停的(死循环)输出该线程的ID 和执行计数,执行计数会不停的增长以表示该线程在不停的运行。可以按照下面的步骤查看一下 loop命令执行的效果:

(1) 按 F7 生成在本实验 3.1 中创建的 EOS Kernel 项目。

(2) 按 F5 启动调试。

(3) 待 EOS 启动完毕,在 EOS 控制台中输入命令“loop ”后按回车。

(4) 结束此次调试。

接下来按照下面的步骤调试线程状态转换的过程:

(1) 在 ke/sysproc.c 文件的 LoopThreadFunction 函数中,开始死循环的代码行(第 787 行)添加一个断点。

(2)按 F5 启动调试。

(3) 待 EOS 启动完毕,在 EOS 控制台中输入命令“loop ”后按回车。

EOS 会在断点处中断执行,表明 loop 线程已经开始死循环了。此时,EOS 中所有的系统线程要么处于就绪状态(其优先级一定小于 8,例如系统空闲线程) ,要么就处于阻塞状态(例如控制台派遣线程或控制台线程) ,所以,只有优先级为 8 的 loop 线程能够在处理器上执行。接下来按照下面的步骤对断点进行一些调整:

(1)删除所有断点。

(2)打开 ps/sched.c 文件,在与线程状态转换相关的函数中添加断点,这样,一旦有线程的状态发

生改变,EOS 会中断执行,就可以观察线程状态转换的详细过程了。需要添加的断点有:

在 PspReadyThread 函数体中添加一个断点(第 130 行) 。

在 PspUnreadyThread 函数体中添加一个断点(第 158 行) 。

在 PspWait 函数体中添加一个断点(第 223 行) 。

在 PspUnwaitThread 函数体中添加一个断点(第 282 行) 。

在 PspSelectNextThread 函数体中添加一个断点(第 395 行) 。

(3)按 F5 继续执行,然后激活虚拟机窗口。

此时在虚拟机窗口中会看到 loop 线程在不停执行,而之前添加的断点都没有被命中,说明此时还没有任何线程的状态发生改变。在开始观察线程状态转换过程之前还有必要做一个说明。在后面的练习中,会在 loop 线程执行的过 程中按一次空格键,这会导致 EOS 依次执行下面的操作:

(1)控制台派遣线程被唤醒,由阻塞状态进入就绪状态。

(2)loop 线程由运行状态进入就绪状态。

(3) 控制台派遣线程由就绪状态进入运行状态。

(4) 待控制台派遣线程处理完毕由于空格键被按下而产生的键盘事件后,派遣线程会由运行状态重新

进入阻塞状态,开始等待下一个键盘事件到来。

(5)loop 线程由就绪状态进入运行状态,继续执行死循环

2.1,线程由阻塞状态进入就绪状态

按照下面的步骤调试线程状态转换的过程:

1. 在虚拟机窗口中按下一次空格键。

2. 此时 EOS 会在 PspUnwaitThread 函数中的断点处中断。在“调试”菜单中选择“快速监视” ,在快速监视对话框的表达式编辑框中输入表达式“*Thread” ,然后点击“重新计算”按钮,即可查看线程控制块(TCB )中的信息。其中 State 域的值为 3(Waiting ) ,双向链表项 StateListEntry的 Next 和 Prev 指针的值都不为 0,说明这个线程还处于阻塞状态,并在某个同步对象的等待队 列中;StartAddr 域的值为 IopConsoleDispatchThread,说明这个线程就是控制台派遣线程。

3. 关闭快速监视对话框,激活“调用堆栈”窗口。根据当前的调用堆栈,可以看到是由键盘中断服务程序(KdbIsr )进入的。当按下空格键后,就会发生键盘中断,从而触发键盘中断服务程序。在该服务程序的最后中会唤醒控制台派遣线程,将键盘事件派遣到活动的控制台。

4. 在“调用堆栈”窗口中双击 PspWakeThread 函数对应的堆栈项。可以看到在此函数中连续调用了PspUnwaitThread 函数和 PspReadyThread 函数, 从而使处于阻塞状态的控制台派遣线程进入就绪状态。

5. 在“调用堆栈”窗口中双击 PspUnwaitThread 函数对应的堆栈项,先来看看此函数是如何改变线程状态的。按 F10 单步调试直到此函数的最后,然后再从快速监视对话框中观察“*Thread”表达式的值。此时 State 域的值为 0(Zero ) ,双向链表项 StateListEntry 的 Next 和 Prev 指针的值都为 0,说明这个线程已经处于游离状态,并已不在任何线程状态的队列中。仔细阅读PspUnwaitThread 函数中的源代码,理解这些源代码是如何改变线程状态的。

6. 按 F5 继续执行,在 PspReadyThread 函数中的断点处中断。按 F10 单步调试直到此函数的最后,然后再从快速监视对话框中观察“*Thread”表达式的值。此时 State 域的值为 1(Ready ) ,双向链表项 StateListEntry 的 Next 和 Prev 指针的值都不为 0,说明这个线程已经处于就绪状态,并已经被放入优先级为 24 的就绪队列中。仔细阅读 PspReadyThread 函数中的源代码,理解这些源

代码是如何改变线程状态的。通过以上的调试,可以将线程由阻塞状态进入就绪状态的步骤总结如下:

(1) 将线程从等待队列中移除。

(2) 将线程的状态由 Waiting 修改为 Zero。

(3) 将线程插入其优先级对应的就绪队列的队尾。

(4) 将线程的状态由 Zero 修改为 Ready。

至此,控制台派遣线程已经进入就绪状态了,因为其优先级(24)比当前处于运行状态的 loop 线程的优先级(8)要高,根据 EOS 已实现的基于优先级的抢先式调度算法,loop 线程会进入就绪状态,控制台派遣线程会抢占处理器从而进入运行状态。接下来调试这两个转换过程。

2.2,线程由运行状态进入就绪状态

按照下面的步骤调试线程状态转换的过程:

1. 按 F5 继续执行,在 PspSelectNextThread 函数中的断点处中断。在快速监视对话框中查看“*PspCurrentThread”表达式的值,观察当前占用处理器的线程的情况。其中 State 域的值为 2(Running ) ,双向链表项 StateListEntry 的 Next 和 Prev 指针的值都为 0,说明这个线程仍然处于运行状态,由于只能有一个处于运行状态的线程,所以这个线程不在任何线程状态的队列中;StartAddr 域的值为 LoopThreadFunction,说明这个线程就是 loop 线程。注意,在本次断点被命中之前,loop 线程就已经被中断执行了,并且其上下文已经保存在线程控制块中。

2. 按 F10 单步调试,直到对当前线程的操作完成(也就是花括号中的操作完成) 。再从快速监视对话框中查看“*PspCurrentThread”表达式的值。其中 State 域的值为 1(Ready ) ,双向链表项StateListEntry 的 Next 和 Prev 指针的值都不为 0,说明 loop 线程已经进入了就绪状态,并已经被放入优先级为 8 的就绪队列中。 仔细阅读 PspSelectNextThread 函数这个花括号中的源代码, 理解这些源代码是如何改变线程状态的,并与 PspReadyThread 函数中的源代码进行比较,说明这两段源代码的异同,体会为什么在这里不能直接调用 PspReadyThread 函数。通过以上的调试,可以将线程由运行状态进入就绪状态的步骤总结如下:

(1) 线程中断运行,将线程中断运行时的上下文保存到线程控制块中。

(2) 如果处于运行状态的线程被更高优先级的线程抢先,就需要将该线程插入其优先级对应的就绪队列的队首。 (注意,如果处于运行状态的线程主动让出处理器,例如时间片用完,就需要将程插入其优先级对应的就绪队列的队尾。 )

(3) 将线程的状态由 Running 修改为 Ready。至此, loop 线程已经进入就绪状态了, 接下来调试控制台派遣线程会得到处理器进入运行状态的过程。

2.3: 线程由就绪状态进入运行状态

按照下面的步骤调试线程状态转换的过程:

1. 按 F5 继续执行, 在 PspUnreadyThread 函数中的断点处中断。 在快速监视对话框中查看 “*Thread”表达式的值。其中 State 域的值为 1(Ready ) ,双向链表项 StateListEntry 的 Next 和 Prev 指针的值都不为 0,说明这个线程处于就绪状态,并在优先级为 24 的就绪队列中;StartAddr 域的值为 IopConsoleDispatchThread , 说明这个线程就是控制台派遣线程。 仔细阅读 PspUnreadyThread 函数中的源代码,理解这些源代码是如何改变线程状态的。

2. 关闭快速监视对话框后,在“调用堆栈”窗口中激活 PspSelectNextThread 函数对应的堆栈项,可以看到在 PspSelectNextThread 函数中已经将 PspCurrentThread 全局指针指向了控制台派遣线程,并在调用 PspUnreadyThread 函数后,将当前线程的状态改成了 Running。

3. 在“调用堆栈”窗口中激活 PspUnreadyThread 函数对应的堆栈项,然后按 F10 单步调试,直到返回 PspSelectNextThread 函数并将线程状态修改为 Running 。再从快速监视对话框中查看“*PspCurrentThread”表达式的值,观察当前占用处理器的线程的情况。其中 State 域的值为 2(Running ) ,双向链

表项 StateListEntry 的 Next 和 Prev 指针的值都为 0,说明控制台派遣线程已经处于运行状态了。接下来,会将该线程的上下文从线程控制块(TCB )复制到处理器的各个寄存器中,处理器就可以从该线程上次停止运行的位置继续运行了。通过以上的调试,可以将线程由就绪状态进入运行状态的步骤总结如下:

(1) 将线程从其优先级对应的就绪队列中移除。

(2) 将线程的状态由 Ready 修改为 Zero。

(3) 将线程的状态由 Zero 修改为 Running。

(4) 将线程的上下文从线程控制块(TCB )复制到处理器的各个寄存器中,让线程从上次停止运行的位置继续运行。至此,控制台派遣线程已经开始运行了。因为此时没有比控制台派遣线程优先级更高的线程来抢占处理器,所以控制台派遣线程可以一直运行,直到将此次由于空格键被按下而产生的键盘事件处理完毕,然后控制台派遣线程会由运行状态重新进入阻塞状态,开始等待下一个键盘事件到来。

2.4,线程由运行状态进入阻塞状态:

按照下面的步骤调试线程状态转换的过程:

1. 按 F5 继续执行, 在 PspWait 函数中的断点处中断。 在快速监视对话框中查看 “*PspCurrentThread”

表达式的值,观察当前占用处理器的线程的情况。其中 State 域的值为 2(Running ) ,双向链表项 StateListEntry 的 Next 和 Prev 指针的值都为 0, 说明这个线程仍然处于运行状态; StartAddr 域的值为 IopConsoleDispatchThread ,说明这个线程就是控制台派遣线程。

2. 按 F10 单步调试,直到左侧的黄色箭头指向代码第 248 行。再从快速监视对话框中查看“*PspCurrentThread”表达式的值。其中 State 域的值为 3(Waiting ) ,双向链表项StateListEntry 的 Next 和 Prev 指针的值都不为 0,说明控制台派遣线程已经处于阻塞状态了,并在某个同步对象的等待队列中。第 248 行代码可以触发线程调度功能,会中断执行当前已经处于阻塞状态的控制台派遣线程,并将处理器上下文保存到该线程的线程控制块中。通过以上的调试,可以将线程由运行状态进入阻塞状态的步骤总结如下:

(1) 将线程插入等待队列的队尾。

(2) 将线程的状态由 Running 修改为 Waiting。

(3) 将线程中断执行,并将处理器上下文保存到该线程的线程控制块中。至此,控制台派遣线程已经进入阻塞状态了。因为此时 loop 线程是就绪队列中优先级最高的线程,线程调度功能会选择让 loop 线程继续执行。按照下面的步骤调试线程状态转换的过程:

1. 按 F5 继续执行,与本实验 3.2.3 节中的情况相同,只不过这次变为 loop 线程由就绪状态进入运行状态。

2. 再按 F5 继续执行,EOS 不会再被断点中断。激活虚拟机窗口,可以看到 loop 线程又开始不停的执行死循环了。

3. 可以再次按空格键,将以上的调试步骤重复一遍。这次调试的速度可以快一些,仔细体会线程状态转换的过程。

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

OS 已经实现了一个 suspend 命令, 其命令函数为

ConsoleCmdSuspendThread (在 ke/sysproc.c 文件的第 843 行) 。在这个命令中调用了 Suspend 原语(在 ps/psspnd.c 文件第 27 行的 PsSuspendThread 函数中实现) 。Suspend 原语可以将一个处于就绪状态的线程挂起。以 loop 线程为例,当使用 suspend 命令将

其挂起时,loop 线程的执行计数就会停止增长。按照下面的步骤观察 loop 线程被挂起的情况:

1. 删除之前添加的所有断点。

2. 按 F5 启动调试。

3. 待 EOS 启动完毕,在 EOS 控制台中输入命令“loop ”后按回车。此时可以看到 loop 线程的执行计数在不停增长,说明 loop 线程正在执行。记录下 loop 线程的 ID。

4. 按 Ctrl+F2 切换到控制台 2,输入命令“suspend 31” (如果 loop 线程的 ID 是 31)后按回车。

5. 按 Ctrl+1 切换回控制台 1,可以看到由于 loop 线程已经成功被挂起,其执行计数已经停止增长了。此时占用处理器的是 EOS 中的空闲线程。

四,实验收获及感悟:

通过本次实验,我学会了调试线程在各种状态间的转化过程,熟悉了线程的状态和转换,在实验中我通过为线程增加挂起状态,加深了对线程状态的理解。

五.实验截图:

实验5 进程的同步

一. 实验目的

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

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

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

二. 实验内容

1 准备实验

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

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

3.1 创建信号量

3.2 等待、释放信号量

3.2.1 等待信号量(不阻塞)

3.2.2 释放信号量(不唤醒)

3.2.3 等待信号量(阻塞)

3.2.4 释放信号量(唤醒)

4 修改EOS 的信号量算法

三. 实验结果

1. 按照下面的步骤查看生产者-消费者同步执行的过程:

(1)使用pc.c 文件中的源代码,替换之前创建的EOS 应用程序项目中EOSApp.c 文件内的源代码。

(2)按F7生成修改后的EOS 应用程序项目。

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

(4)在调试异常对话框中选择“否”,继续执行。

(5)立即激活虚拟机窗口查看生产者-消费者同步执行的过程

(6)待应用程序执行完毕后,结束此次调试。

2. 等待、释放信号量

(1)等待信号量(不阻塞)

生产者和消费者刚开始执行时,用来放产品的缓冲区都是空的,所以生产者在第一次调用WaitForSingleObject 函数等待Empty 信号量时,应该不需要阻塞就可以立即返回。

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

生产者线程通过等待Empty 信号量使空缓冲区数量减少了1,通过释放Full 信号量使满缓冲区数量增加了1,这样就表示生产者线程生产了一个产品并占用了一个缓冲区。

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

由于开始时生产者线程生产产品的速度较快,而消费者线程消费产品的速度较慢,所以当缓冲池中所有的缓冲区都被产品占用时,生产者在生产新的产品时就会被阻塞

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

只有当消费者线程从缓冲池中消费了一个产品,从而产生一个空缓冲区后,生产者线程才会被唤醒并继续生产14号产品。

四. 实验总结

今天的实验的内容是进程的同步,这也是进程这一概念提出后面临的一个非常大的问题,因为当进程在竞争临界资源时,由于进程的异步性,便会给系统造成混乱,所以能不能让诸多进程之间有效的共享资源和相互合作,就变成了一个很重要的问题。今天的实验也让我们直接的观察到了EOS 操作系统如何通过信号量机制解决进程同步问题,并对程序进行修改,这对我们今后的学习有很大的帮助,但对于解决大量同步操作带来的系统死锁问题,我还是欠缺实践和认识。

五. 实验截图

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

一,实验目的:

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

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

二,预备知识:

阅读本书第 6 章。重点阅读第 6.3 节和第 6.6 节,了解物理存储器的管理方式和进程逻辑地址空间的管理方式。

三,主要实验内容及步骤:

1,准备实验;

2,阅读控制台命令“ pm ”相关的源代码,并查看其执行的结果; 3,分配物理页和释放物理页:

接下来,在 pm 命令函数中添加分配物理页和释放物理页的代码,单步调试管理物理页的方法。按照

下面的步骤修改 pm 命令的源代码:

1. 使用 OS Lab 打开本实验文件夹中的 pm.c 文件(将文件拖动到 OS Lab 窗口中释放即可打开) 。此文件中有一个修改后的 ConsoleCmdPhysicalMemory 函数,主要是在原有代码的后面增加了分配物理页和释放物理页的代码。

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

3. 按 F7 生成修改后的 EOS Kernel 项目。

4. 按 F5 启动调试。

5. 待 EOS 启动完毕,在 EOS 控制台中输入命令“pm ”后按回车。

按照下面的步骤调试分配物理页和释放物理页的过程:

1. 结束之前的调试。

2. 在 ke/sysproc.c 文件的 ConsoleCmdPhysicalMemory 函数中,在调用 MiAllocateAnyPages 函数的代码行(第 1103 行)添加一个断点,在调用 MiFreePages 函数的代码行(第 1115 行)添加一个断点。

3. 按 F5 启动调试。

4. 待 EOS 启动完毕,在 EOS 控制台中输入命令“pm ”后按回车。

5. pm 命令开始执行后,会在调用 MiAllocateAnyPages 函数的代码行处中断,按 F11 调试进入MiAllocateAnyPages 函数。

6. 按 F10 单步调试 MiAllocateAnyPages 函数的执行过程,尝试回答下面的问题:

(1) 本次分配的物理页的数量是多少?分配的物理页的页框号是多少?

(2) 物理页是从空闲页链表中分配的?还是从零页链表中分配的?

(3) 哪一行语句减少了空闲页的数量?哪一行语句将刚刚分配的物理页由空闲状态修改为忙状态?

(4) 绘制 MiAllocateAnyPages 函数的流程图。

继续调试释放物理页的过程:

1. 按 F5 继续执行,会在调用 MiFreePages 函数的代码行处中断,按 F11 调试进入 MiFreePages 函数。

2. 按 F10 单步调试 MiFreePages 函数的执行过程,尝试回答下面的问题:

(1) 本次释放的物理页的数量是多少?释放的物理页的页框号是多少?释放的物理页是之前分配的物理页吗?

(2) 释放的物理页是被放入了空闲页链表中?还是零页链表中?

(3) 绘制 MiFreePages 函数的流程图。

结束此次调试。 继续修改 pm 命令的源代码, 尝试在调用 MiAllocateAnyPages 函数时分配多个物理页,然后在调用 MiFreePages 函数时将分配的多个物理页释放,并练习调试这两个函数在分配多个物理页和释放多个物理页时执行的过程

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

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

接下来, 在 vm 命令函数中添加分配虚拟页和释放虚拟页的代码, 单步调试管理虚拟页的方法。 首先,按照下面的步骤修改 vm 命令的源代码:

1. 使用 OS Lab 打开本实验文件夹中的 vm.c 文件(将文件拖动到 OS Lab 窗口中释放即可打开) 。此文件中有一个修改后的 ConsoleCmdVM 函数,主要是在原有代码的后面增加了分配虚拟页和释放物理页的代码。

2. 使用 vm.c 文件中 ConsoleCmdVM 函数的函数体替换 ke/sysproc.c 文件中 ConsoleCmdVM 函数的函数体。

3. 按 F7 生成修改后的 EOS Kernel 项目。

4. 按 F5 启动调试。

5. 待 EOS 启动完毕,在 EOS 控制台中输入命令“vm 1”后按回车。

命令执行的结果会同时转储在“输出”窗口中。尝试说明分配虚拟页或者释放虚拟页后虚拟地址描述符以及物理存储器的变化情况。

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

四,实验收获及感悟:

通过本次实验,让我学会了通过查看物理存储器的使用情况,并练习分配和回收物理内存,从而掌握物理存储器的管理方法。以及通过查看进程逻辑地址空间的使用情况,并练习分配和回收虚拟内存,从而掌握进程逻辑地址空间的管理方法。

五:实验截图:

实验8:分页存储器的管理

一,实验目的:

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

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

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

二,预备知识:

阅读本书第 6 章。了解 i386 处理器的二级页表硬件机制,EOS 操作系统的分页存储器管理方式,以及进程地址空间的内存分布。

三,主要实验内容及步骤:

1,准备实验;

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

使用 OS Lab 打开本实验文件夹中的 memory.c 和 getcr3.asm 文件(将文件拖动到 OS Lab 窗口中释放即可打开) 。仔细阅读这两个文件中的源代码和注释,main 函数的流程图可以参见图 16-1。

按照下面的步骤查看 EOS 应用程序进程的页目录和页表:

1. 使用 memory.c 文件中的源代码替换之前创建的 EOS 应用程序项目中 EOSApp.c 文件中的源代码。

2. 右键点击“项目管理器”窗口中的“源文件”文件夹节点,在弹出的快捷菜单中选择“添加”中的“添加新文件”。

3. 在弹出的“添加新文件”对话框中选择“asm 源文件”模板。

4. 在“名称”中输入文件名称“func ”。

5. 点击“添加”按钮添加并自动打开文件 func.asm。

6. 将 getcr3.asm 文件中的源代码复制到 func.asm 文件中。

7. 按 F7 生成修改后的 EOS 应用程序项目。

8. 按 F5 启动调试。

9. 应用程序执行的过程中,会将该进程的二级页表映射信息输出到虚拟机窗口和 OS Lab“输出”窗口中,输出内容如图 16-2(a ) 。

10. 将“输出”窗口中的内容复制到一个文本文件中。

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

需要对 EOS 应用程序进行一些修改:

1. 结束之前的调试。

2. 取消 EOSApp.c 第 121 行语句的注释(该行语句会等待 10 秒) 。

3. 按 F7 生成修改后的 EOS 应用程序项目。

4. 按 F5 启动调试。

5. 在“Console-1”中会自动执行 EOSApp.exe,创建该应用程序进程。利用其等待 10 秒的时间,按 Ctrl+F2 切换到“Console-2” 。

6. 在“Console-2”中输入命令“mm ”后按回车,会将系统进程的二级页表映射信息输出到虚拟机窗口和 OS Lab 的“输出”窗口,输出内容如图 16-2(b ) 。注意,在图 16-2(b )中添加了一些空行,方便与图 16-2(a )比较。 “Console-1”中的应用程序在等待 10 秒后,又会输出和图 16-2(a )一致的内容。

7. 将“输出”窗口中的内容复制到一个文本文件中。

控制台命令“mm ”对应的源代码在 EOS 内核项目 ke/sysproc.c 文件的 ConsoleCmdMemoryMap 函数中(第 382 行) 。阅读这部分源代码后会发现,其与 EOSApp.c 文件中的源代码基本类似。

结合图 16-2(a )和(b )回答下面的问题:

EOS 启动后系统进程是一直运行的, 所以当创建应用程序进程后, 系统中就同时存在了两个进程,这两个进程是否有各自的页目录?在页目录映射的页表中,哪些是独占的,哪些是共享的?分析其中的原因。

统计当应用程序进程和系统进程并发时,总共有多少物理页被占用?

应用程序结束后,在“Console-1”中再次输入命令“mm ” ,查看在没有应用程序进程时,系统进程的页目录和页表。将“输出”窗口中的内容复制到一个文本文件中。将输出的内容与图 16-2(b )比较,思考为什么系统进程(即内核地址空间)占用的物理页会减少?(提示:创建应用程序进程时,EOS 内核要 为其创建 PCB,应用程序结束时,内核要释放 PCB 占用的内存。 ) 4, 查看应用程序进程并发时的页目录和页表

需要对 EOS 应用程序进行一些修改:

1. 结束之前的调试。

2. 取消 EOSApp.c 第 201 行语句的注释(该行语句会等待 10 秒) 。

3. 按 F7 生成修改后的 EOS 应用程序项目。

4. 按 F5 启动调试。

5. 在“Console-1”中会自动执行 EOSApp.exe,创建该应用程序进程。利用其等待 10 秒的时间,按Ctrl+F2 切换到“Console-2” 。

6. 在“Console-2”中输入“eosapp ”后按回车,再执行一个 EOSApp.exe。

7. 由 EOSApp.exe 创建的两个并发进程会先后在各自的控制台和 OS Lab“输出”窗口中,输出各自的二级页表映射信息。输出的内容如图 16-3。

8. 将“输出”窗口中的内容复制到一个文本文件中。

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

下面通过编程的方式,从 EOS 操作系统内核中申请两个未用的物理页,将第一个物理页当作页表,映射基址为 0xE0000000 的 4M 虚拟地址空间,然后将第二个物理页分别映射到基址为 0xE0000000 和0xE0001000 的 4K 虚拟地址空间。从而验证下面的结论:

虽然进程可以访问 4G 虚拟地址空间,但是只有当一个虚拟地址通过二级页表映射关系能够映射到实际的物理地址时,该虚拟地址才能够被访问,否则会触发异常。

所有未用的物理页都是由 EOS 操作系统内核统一管理的,使用时必须向内核申请。

为虚拟地址映射物理页时,必须首先为页目录安装页表,然后再为页表安装物理页。并且只有在刷新快表后,对页目录和页表的更改才能生效。 不同的虚拟地址可以映射相同的物理页,从而实现共享。

四,实验收获及感悟:

通过本次实验,让我学会了查看 EOS 应用程序进程和系统进程的二级页表映射信息,理解页目录和页表的管理方式。并且学会了编程修改页目录和页表的映射关系,理解了分页地址变换原理。

五:实验截图:

实验11:扫描 FAT12 文件系统管理的软盘

一,实验目的:

1,通过查看 FAT12 文件系统的扫描数据,并调试扫描的过程,理解 FAT12 文件系统管理软盘的方式。

2,通过改进 FAT12 文件系统的扫描功能,加深对 FAT12 文件系统的理解。

二,预备知识:

阅读本书第 8 章,学习 FAT12 文件系统技术细节。关于 FAT 文件系统更详细的信息,还可以参阅微软硬件白皮书《FAT: General Overview of On-Disk Format 》 (在 OS Lab 的“帮助”菜单中选择“其它帮助文档”中的“FAT 文件系统概述” ) 。

三,主要实验内容及步骤:

1,准备实验;

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

阅读 ke/sysproc.c 文件中第 1321 行的 ConsoleCmdScanDisk 函数,学习“sd ”命令是如何扫描软盘上的 FAT12 文件系统的。在阅读的过程中需要注意下面几点:

在开始扫描软盘之前要关闭中断,之后要打开中断,这样可以防止在命令执行的过程中有其它线程修改软盘上的数据。

以软盘的盘符“A:”做为 ObpLookupObjectByName 函数的参数,就可以获得 FAT12 文件系统设备对象的指针。

FAT12 文件系统设备对象的扩展块(FatDevice->DeviceExtension)是一个卷控制块(VCB ,在文件 io/driver/fat12.h 的第 115 行定义) ,从其中可以获得文件系统的重要参数,并可以扫描 FAT表。

FatGetFatEntryValue 函数可以根据第二个参数所指定的簇号,返回簇在 FAT 表中对应项的值,在扫描 FAT 表时通过调用此函数来统计空闲簇的数量(FreeClusterCount ) 。

按照下面的步骤执行控制台命令“sd ” ,查看扫描的结果:

1. 按 F7 生成在本实验 3.1 中创建的 EOS Kernel 项目。

2. 按 F5 启动调试。

3. 待 EOS 启动完毕,在 EOS 控制台中输入命令“sd ”后按回车。

观察命令执行的结果,如图 19-1 所示,可以了解 FAT12 文件系统的信息。

3,根据 BPB 中的信息计算出其他信息

修改“sd ”命令函数 ConsoleCmdScanDisk 的源代码,在输出 BPB 中保存的信息后,不再通过pVcb->FirstRootDirSector 等变量的值进行打印输出,而是通过 BPB 中保存的信息重新计算出下列信息,并打印输出:

(1) 计算并打印输出根目录的起始扇区号,即 pVcb->FirstRootDirSector 的值。

(2) 计算并打印输出根目录的大小,即 pVcb->RootDirSize 的值。

(3) 计算并打印输出数据区的起始扇区号,即 pVcb->FirstDataSector 的值。

(4) 计算并打印输出数据区中簇的数量,即 pVcb->NumberOfClusters 的值。

4, 阅读控制台命令“ dir ”相关的源代码,并查看其执行结果; 按照下面的步骤执行控制台命令“dir ” ,查看扫描的结果:

1. 按 F7 生成在本实验 3.1 中创建的 EOS Kernel 项目。

2. 按 F5 启动调试。

3. 待 EOS 启动完毕,在 EOS 控制台中输入命令“dir ”后按回车。 观察命令执行的结果,可以看到当前软盘中存储的文件的信息。

5,输出每个文件所占用的磁盘空间的大小

修改 “dir ” 命令函数 ConsoleCmdDir 的源代码, 要求在输出每个文件的名称、 大小、 最后改写时间后,再输出每个文件所占用的磁盘空间(以字节为单位) 。

四,实验收获及感悟:

通过本次实验,让我学会了如何通过查看 FAT12 文件系统的扫描数据,并调试扫描的过程,理解 FAT12 文件系统管理软盘的方式。以及通过改进 FAT12 文件系统的扫描功能,加深了对 FAT12 文件系统的理解。

五:实验截图:

合肥工业大学

计算机与信息学院

实验报告

课 程: 计算机操作系统

专业班级: 计算机科学与技术2班

学 号:

姓 名:

实验1 实验环境的使用

一. 实验目的

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

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

二. 实验内容

1.启动OS Lab

2. 学习OS Lab的基本使用方法

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

2.2生成项目

2.3执行项目

2.4调试项目

2.4.1 使用断点中断执行

2.4.2单步调试

2.4.3查看变量的值

2.4.4调用堆栈

3. EOS内核项目的生成和调试

3.1新建EOS 内核项目

3.2生成项目

3.3调试项目

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

3.5查看EOS SDK(Software Development Kit)文件夹

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

4.1新建EOS 应用程序项目

4.2生成项目

4.3调试项目

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

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

5 退出OS Lab

6 保存EOS 内核项目

三. 实验结果

本实验主要是熟悉EOS 操作系统的基本操作,练习了:

(1)新Windows 控制台应用程序项,

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

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

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

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

新建完毕后, OS Lab 会自动打开这个新建的项目。

(2)在“生成”菜单中选择“生成项目”。

结果如图

(3)执行项目:选择“调试”菜单中的“开始执行”

(4)调试项目:

1. 右键点击“项目管理器”窗口中的“源文件”文件夹节点,在弹出的快捷菜单中选择“添加”中的“添加新文件”。

2. 在弹出的“添加新文件”对话框中选择“C 源文件”模板。

3. 在“名称”中输入文件名称“func ”。

4. 点击“添加”按钮,添加并自动打开文件func.c ,此时的“项目管理器”窗口会如图:

(5). 在 func.c 文件中添加函数:

int Func (int n) { n = n + 1;

return n; }

(6). 点击源代码编辑器上方的console.c 标签,切换到console.c 文件。将 main 函数修改为:

int main (int argc, char* argv[])

{ int Func (int n); // 声明Func 函数

int n = 0; n = Func(10);

printf ("Hello World!\n");

return 0; }

代码修改完毕后按F7

实验结果为输出:Hello World!

(7). 在main 函数中定义变量n 的代码行

int n = 0; 上点击鼠标右键,在弹出的快捷菜单中选择“插入/删除断点”,会在此行左侧的空

白处显示一个红色

圆点,表示已经成功

在此行代码添加了一个断点

练习使用“逐过程”, “逐语句”,“跳出”功能

(8). 在源代码编辑器中变量n 的名称上点击鼠标右键,在弹出的快捷菜单中选择“快速监视”,进行单步测试,观察n 结果依次为0,11

(9)调用堆栈,选择“调试”菜单“窗口”中的“调用堆栈”,激活“调用堆栈”窗口。可以看到当前“调用堆栈”窗口中只有一个main 函数(显示的内容还包括了参数值和函数地址)。

按F11(“逐语句”功能的快捷键)调试,直到进入Func 函数 ,其中当前正在调试的Func 函数在栈顶位置,main 函数在栈底位置。说明是在main 函数中调用了Func 函数。

(10)查看软盘镜像文件中的内容,在“项目管理器”窗口中双击软盘镜像文件Floppy.img

四. 实验总结

今天第一次进行操作系统这门课的实验,这也是将抽象的理论知识应用到实践的一个很好的机会,同时,我们也学习了使用OS Lab 的这个实验环境,OS Lab 的操作界面和Microsoft Visual Studio 2010很像,所以使用起来很快就能上

手,对于实验内容,今天主要练习了:新建Windows 控制台应用程序项目、生成项目、执行项目、调试项目以及EOS 应用程序项目的生成和调试;设置间断点,并且在它的基础上进行了单步操作;详细观察了通过“快速监视“标记的数的值的变化过程等基本操作,这也为以后实验的顺利进行打下了良好的基础。

五. 实验截图

实验2 操作系统的启动

一. 实验目的

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

2. 查看EOS 启动后的状态和行为,理解操作系统启动后的工作方式。 二. 实验内容 1 准备实验

2 调试EOS 操作系统的启动过程 2.1 使用Bochs 做为远程目标机 2.2 调试BIOS 程序 2.3 调试软盘引导扇区程序 2.4 调试加载程序 2.5 调试内核

2.6 EOS启动后的状态和行为

三. 实验结果

1. 新建一个EOS Kernel项目。 2. 使用Bochs 做为远程目标机

(1)在“项目管理器”窗口中,右键点击项目节点,在弹出的快捷菜单中选择“属性”。

(2) 在弹出的“属性页”对话框右侧的属性列表中找到“远程目标机”属性,将此属性值修改为“Bochs Debug”

(3)点击“确定”按钮关闭“属性页”对话框。接下来就可以使用Bochs 模拟器调试BIOS 程序和软盘引导扇区程序了。

3. 按F5启动调试,此时会弹出两个Bochs 窗口。标题为“Bochs for windows - Display ”的窗口相当于计算机的显示器,显示操作系统的输出。标题为“Bochs for windows - Console”的窗口是Bochs 的控制台,用来输入调试命令,输出各种调试信息。

4. 启动调试后,Bochs 在CPU 要执行的第一条指令(即BIOS 的第一条指令)处中断。 此时,Display 窗口没有显示任何内容,Console 窗口显示要执行的BIOS 第一条指令的相关信息,并等待用户输入调试命令

5. 然后查看CPU 在没有执行任何指令之前主要寄存器中的数据,以及内存中的数据

(1)在Console 窗口中输入调试命令sreg 后按回车,显示当前CPU 中各个段寄存器的值

(2)输入调试命令r 后按回车,显示当前CPU 中各个通用寄存器的值 (3)输入调试命令xp /1024b 0x0000,查看开始的1024个字节的物理内存。在Console 中输出的这1K 物理内存的值都为0,说明BIOS 中断向量表还没有被加载到此处。

(4)输入调试命令xp /512b 0x7c00,查看软盘引导扇区应该被加载到的内存位置。输出的内存值都为0,说明软盘引导扇区还没有被加载到此处。

6.EOS 启动后的状态和行为

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

结果如图

(2)查看EOS 启动后的进程和线程的信息: 在控制台中输入命令“pt ”后按回车。 输出的进程和线程信息如图所示

四. 实验总结

今天的实验在实验一的基础上学习了EOS 操作系统的启动、跟踪调试以及EOS 在PC 机上从加电复位到成功启动的全过程,这让我们对EOS 这个平台有了初步的掌握,同时我们也练习了查看了EOS 启动后的状态和行为,学会了查看CPU 主要寄存器中以及内存中的数据的方法。 五. 实验截图

实验3进程的创建

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

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

二,预备知识:

阅读本书第 5.1 节,重点理解程序和进程的关系,熟悉进程控制块结构体以及进程创建的过程。仔细

学习CreateProcess 函数和其它与创建进程相关的函数的说明, 注意理解这些函数的参数和返回值的意义。

三,主要实验内容及步骤: 1, 准备实验;

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

练习使用控制台命令创建 EOS 应用程序进程的具体步骤如下:

(1) 在 EOS 应用程序项目的“项目管理器”窗口中双击 Floppy.img 文件,使用 FloppyImageEditor 工具打开此软盘镜像文件。

(2)将本实验文件夹中的 Hello.exe 文件拖动到 FloppyImageEditor 工具窗口的文件列表中释放,

Hello.exe 文件即被添加到软盘镜像文件中。Hello.exe 一个 EOS 应用程序,其源代码可以参见

本实验文件夹中的 Hello.c 源文件。

(3)在 FloppyImageEditor 中选择“文件”菜单中的“保存”后关闭 FloppyImageEditor 。

(4)按 F7 生成 EOS 应用项目。 (5)按 F5 启动调试。OS Lab 会弹出一个调试异常对话框,并中断应用程序的执行。

(6) 在调试异常对话框中选择“否”,忽略异常继续执行应用程序。

(7) 激活虚拟机窗口,待该应用程序执行完毕后,在 EOS 的控制台中输入命令“A:\Hello.exe”后回 车。

(8) Hello.exe 应用程序开始执行,观察其输出如图 11-1。

(9)待 Hello.exe 执行完毕后可以重复第 7 步,或者结束此次调试。

3. 练习通过编程的方式让应用程序创建另一个应用程序的进程 使用 OS Lab 打开本实验文件夹中的 NewProc.c 文件(将此文件拖动到 OS Lab 窗口中释放即可)

按照下面的步骤查看应用程序创建另一个应用程序的进程的执行结果:

(1) 使用NewProc.c 文件中的源代码替换之前创建的EOS 应用程序项目中的EOSApp.c 文件内的源代码。

(2)按 F7 生成修改后的 EOS 应用程序项目。

(3) 按 F5 启动调试。OS Lab 会首先弹出一个调试异常对话框。 (4) 在调试异常对话框中选择“否”,继续执行。 (5)激活虚拟机窗口查看应用程序输出的内容, 如图 11-2。 结合图 11-1, 可以看到父进程 (EOSApp.exe )

首先开始执行并输出内容,父进程创建了子进程(Hello.exe )后,子进程开始执行并输出内容,

待子进程结束后父进程再继续执行。 (6)结束此次调试。

4. 调试 CreateProcess 函数

按照下面的步骤调试 CreateProcess 函数创建进程的过程: (1) 按 F5 启动调试 EOS 应用程序,OS Lab 会首先弹出一个调试异常对话框。 (2) 选择“是”调试异常,调试会中断。

(3)在 main 函数中调用 CreateProcess 函数的代码行(第 57 行)添加一个断点。

(4)按 F5 继续调试,在断点处中断。 (5)按 F11 调试进入 CreateProcess 函数。此时已经开始进入 EOS 内核进行调试。

.5, 调试PsCreateProcess 函数

创建进程最主要的操作就是创建进程控制块(PCB),并初始化其中的各种信息(也就是为进程分配各种资源) 。所以在 PsCreateProcess 函数中首先调

用了 PspCreateProcessEnvironment 函数来创建进程控制块。

调试 PspCreateProcessEnvironment 函数的步骤如下:

(1)在 PsCreateProcess 函数中找到调用 PspCreateProcessEnvironment 函数的代码行(create.c

文件的第 163 行) ,并在此行添加一个断点。

(2) 按 F5 继续调试,到此断点处中断。

(3) 按 F11 调试进入 PspCreateProcessEnvironment 函数。

由于 PspCreateProcessEnvironment 函数的主要功能是创建进程控制块并初始化其中的部分信息,所

以在此函数的开始,定义了一个进程控制块的指针变量 NewProcess。在此函数中查找到创建进程控制块的

代码行(create.c 文件的第 418 行)

Status = ObCreateObject( PspProcessType,

NULL,

sizeof(PROCESS) + ImageNameSize + CmdLineSize,

0,

(PVOID*)&NewProcess );

这里的 ObCreateObject 函数会在由 EOS 内核管理的内存中创建了一个新的进程控制块(也就是分配了一

块内存) ,并由 NewProcess 返回进程控制块的指针(也就是所分配内存的起始地址) 。

按照下面的步骤调试进程控制块的创建过程:

(1)在调用 ObCreateObject 函数的代码行(create.c 文件的第 418 行)添加一个断点。

(2)按 F5 继续调试,到此断点处中断。

(3) 按 F10 执行此函数后中断。

(4) 此时为了查看进程控制块中的信息,将表达式*NewProcess 添加到“监视”窗口中。

(5) 将鼠标移动到“监视”窗口中此表达式的“值”属性上,会弹出一个临时窗口,在临时窗口中会

按照进程控制块的结构显示各个成员变量的值(可以参考 PROCESS 结构体的定义) 。由于只是新

建了进程控制块,还没有初始化其中成员变量,所以值都为 0。

接下来调试初始化进程控制块中各个成员变量的过程:

(1)首先创建进程的地址空间,即 4G 虚拟地址空间。在代码行(create.c 文件的第 437 行)

NewProcess->Pas = MmCreateProcessAddressSpace();

添加一个断点。

(2) 按 F5 继续调试,到此断点处中断。

(3)按 F10 执行此行代码后中断。

(4)在“监视”窗口中查看进程控制块的成员变量 Pas 的值已经不再是 0。说明已经初始化了进程的

4G 虚拟地址空间。

(5)使用 F10 一步步调试 PspCreateProcessEnvironment 函数中后面的代码,

在调试的过程中根据执

行的源代码,查看“监视”窗口中*NewProcess 表达式的值,观察进程控制块中哪些成员变量是

被哪些代码初始化的,哪些成员变量还没有被初始化。

(6)当从 PspCreateProcessEnvironment 函数返回到 PsCreateProcess 函数后, 停止按 F10。 此时 “监

视”窗口中已经不能再显示表达式*NewProcess 的值了,在 PsCreateProcess 函数中是使用

ProcessObject 指针指向进程控制块的,所以将表达式*ProcessObject 添加到“监视”窗口中就

可以继续观察新建进程控制块中的信息。

(7)接下来继续使用 F10 一步步调试 PsCreateProcess 函数中的代码,同样要注意观察执行后的代码

修改了进程控制块中的哪些成员变量。当调试到 PsCreateProcess 函数的最后一行代码时,查看

进程控制块中的信息, 此时所有的成员变量都已经被初始化了 (注意观察成员 ImageName 的值) 。

(8)按 F5 继续执行,EOS 内核会为刚刚初始化完毕的进程控制块新建一个进程。激活虚拟机窗口查看

新建进程执行的结果。

(9)在 OS Lab 中选择“调试”菜单中的“停止调试”结束此次调试。

(10)选择“调试”菜单中的“删除所有断点” 。

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

四.实验收获及感悟:

通过本次实验熟悉了进程的创建,对进程更加的了解。也让我更加熟悉OS Lab 这个软件,对于EOS 操作系统的理解也更加深刻。

五.实验截图

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

一,实验目的:

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

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

二,预备知识:

阅读本书第 5.2.3 节,了解线程都有哪些状态以及 EOS 是如何定义这些状态的。了解线程是如何在这些状态之间进行转换的,特别是要阅读一下 EOS 中用于线程转换的相关函数的源代码。阅读本书第 5.2.4节, 了解 EOS 为线程添加的挂起状态, 以及 Suspend 和 Resume 原语操作。 线程状态的转换和线程的同步、线程的调度是不可分割的,所以建议读者简单学习一下第 5.3 和 5.4 节中的内容。

此外,由于本实验需要观察“控制台派遣线程”在不同状态间的转换过程,所以读者也需要对该线程有一个简单的了解。控制台派遣线程做为一个系统线程(优先级为 24) ,在 EOS 启动后就会被创建,但是

该线程绝大部分时间都处于阻塞状态,只有当发生键盘事件(例如键被按下)时才会被唤醒,当该线程将键盘事件派遣到当前活动的控制台后,该线程就会重新回到阻塞状态等待下一个键盘事件到来。该线程的线程函数是文件 io/console.c 中的 IopConsoleDispatchThread 函数(第 567 行) 。

三,主要实验内容及步骤:

1,准备实验;

2,调试线程状态的转换过程;

在本练习中,会在与线程状态转换相关的函数中添加若干个断点,并引导读者单步调试这些函数,使读者对 EOS 中的下列线程状态转换过程有一个全面的认识: 线程由阻塞状态进入就绪状态。

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

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

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

为了完成这个练习,EOS 准备了一个控制台命令“loop ” ,这个命令的命令函数是 ke/sysproc.c 文件中的 ConsoleCmdLoop 函数(第 797 行) ,在此函数中使用 LoopThreadFunction 函数(第 755 行)创建了一个优先级为 8 的线程(后面简称为“loop 线程” ) ,该线程会在控制台中不停的(死循环)输出该线程的ID 和执行计数,执行计数会不停的增长以表示该线程在不停的运行。可以按照下面的步骤查看一下 loop命令执行的效果:

(1) 按 F7 生成在本实验 3.1 中创建的 EOS Kernel 项目。

(2) 按 F5 启动调试。

(3) 待 EOS 启动完毕,在 EOS 控制台中输入命令“loop ”后按回车。

(4) 结束此次调试。

接下来按照下面的步骤调试线程状态转换的过程:

(1) 在 ke/sysproc.c 文件的 LoopThreadFunction 函数中,开始死循环的代码行(第 787 行)添加一个断点。

(2)按 F5 启动调试。

(3) 待 EOS 启动完毕,在 EOS 控制台中输入命令“loop ”后按回车。

EOS 会在断点处中断执行,表明 loop 线程已经开始死循环了。此时,EOS 中所有的系统线程要么处于就绪状态(其优先级一定小于 8,例如系统空闲线程) ,要么就处于阻塞状态(例如控制台派遣线程或控制台线程) ,所以,只有优先级为 8 的 loop 线程能够在处理器上执行。接下来按照下面的步骤对断点进行一些调整:

(1)删除所有断点。

(2)打开 ps/sched.c 文件,在与线程状态转换相关的函数中添加断点,这样,一旦有线程的状态发

生改变,EOS 会中断执行,就可以观察线程状态转换的详细过程了。需要添加的断点有:

在 PspReadyThread 函数体中添加一个断点(第 130 行) 。

在 PspUnreadyThread 函数体中添加一个断点(第 158 行) 。

在 PspWait 函数体中添加一个断点(第 223 行) 。

在 PspUnwaitThread 函数体中添加一个断点(第 282 行) 。

在 PspSelectNextThread 函数体中添加一个断点(第 395 行) 。

(3)按 F5 继续执行,然后激活虚拟机窗口。

此时在虚拟机窗口中会看到 loop 线程在不停执行,而之前添加的断点都没有被命中,说明此时还没有任何线程的状态发生改变。在开始观察线程状态转换过程之前还有必要做一个说明。在后面的练习中,会在 loop 线程执行的过 程中按一次空格键,这会导致 EOS 依次执行下面的操作:

(1)控制台派遣线程被唤醒,由阻塞状态进入就绪状态。

(2)loop 线程由运行状态进入就绪状态。

(3) 控制台派遣线程由就绪状态进入运行状态。

(4) 待控制台派遣线程处理完毕由于空格键被按下而产生的键盘事件后,派遣线程会由运行状态重新

进入阻塞状态,开始等待下一个键盘事件到来。

(5)loop 线程由就绪状态进入运行状态,继续执行死循环

2.1,线程由阻塞状态进入就绪状态

按照下面的步骤调试线程状态转换的过程:

1. 在虚拟机窗口中按下一次空格键。

2. 此时 EOS 会在 PspUnwaitThread 函数中的断点处中断。在“调试”菜单中选择“快速监视” ,在快速监视对话框的表达式编辑框中输入表达式“*Thread” ,然后点击“重新计算”按钮,即可查看线程控制块(TCB )中的信息。其中 State 域的值为 3(Waiting ) ,双向链表项 StateListEntry的 Next 和 Prev 指针的值都不为 0,说明这个线程还处于阻塞状态,并在某个同步对象的等待队 列中;StartAddr 域的值为 IopConsoleDispatchThread,说明这个线程就是控制台派遣线程。

3. 关闭快速监视对话框,激活“调用堆栈”窗口。根据当前的调用堆栈,可以看到是由键盘中断服务程序(KdbIsr )进入的。当按下空格键后,就会发生键盘中断,从而触发键盘中断服务程序。在该服务程序的最后中会唤醒控制台派遣线程,将键盘事件派遣到活动的控制台。

4. 在“调用堆栈”窗口中双击 PspWakeThread 函数对应的堆栈项。可以看到在此函数中连续调用了PspUnwaitThread 函数和 PspReadyThread 函数, 从而使处于阻塞状态的控制台派遣线程进入就绪状态。

5. 在“调用堆栈”窗口中双击 PspUnwaitThread 函数对应的堆栈项,先来看看此函数是如何改变线程状态的。按 F10 单步调试直到此函数的最后,然后再从快速监视对话框中观察“*Thread”表达式的值。此时 State 域的值为 0(Zero ) ,双向链表项 StateListEntry 的 Next 和 Prev 指针的值都为 0,说明这个线程已经处于游离状态,并已不在任何线程状态的队列中。仔细阅读PspUnwaitThread 函数中的源代码,理解这些源代码是如何改变线程状态的。

6. 按 F5 继续执行,在 PspReadyThread 函数中的断点处中断。按 F10 单步调试直到此函数的最后,然后再从快速监视对话框中观察“*Thread”表达式的值。此时 State 域的值为 1(Ready ) ,双向链表项 StateListEntry 的 Next 和 Prev 指针的值都不为 0,说明这个线程已经处于就绪状态,并已经被放入优先级为 24 的就绪队列中。仔细阅读 PspReadyThread 函数中的源代码,理解这些源

代码是如何改变线程状态的。通过以上的调试,可以将线程由阻塞状态进入就绪状态的步骤总结如下:

(1) 将线程从等待队列中移除。

(2) 将线程的状态由 Waiting 修改为 Zero。

(3) 将线程插入其优先级对应的就绪队列的队尾。

(4) 将线程的状态由 Zero 修改为 Ready。

至此,控制台派遣线程已经进入就绪状态了,因为其优先级(24)比当前处于运行状态的 loop 线程的优先级(8)要高,根据 EOS 已实现的基于优先级的抢先式调度算法,loop 线程会进入就绪状态,控制台派遣线程会抢占处理器从而进入运行状态。接下来调试这两个转换过程。

2.2,线程由运行状态进入就绪状态

按照下面的步骤调试线程状态转换的过程:

1. 按 F5 继续执行,在 PspSelectNextThread 函数中的断点处中断。在快速监视对话框中查看“*PspCurrentThread”表达式的值,观察当前占用处理器的线程的情况。其中 State 域的值为 2(Running ) ,双向链表项 StateListEntry 的 Next 和 Prev 指针的值都为 0,说明这个线程仍然处于运行状态,由于只能有一个处于运行状态的线程,所以这个线程不在任何线程状态的队列中;StartAddr 域的值为 LoopThreadFunction,说明这个线程就是 loop 线程。注意,在本次断点被命中之前,loop 线程就已经被中断执行了,并且其上下文已经保存在线程控制块中。

2. 按 F10 单步调试,直到对当前线程的操作完成(也就是花括号中的操作完成) 。再从快速监视对话框中查看“*PspCurrentThread”表达式的值。其中 State 域的值为 1(Ready ) ,双向链表项StateListEntry 的 Next 和 Prev 指针的值都不为 0,说明 loop 线程已经进入了就绪状态,并已经被放入优先级为 8 的就绪队列中。 仔细阅读 PspSelectNextThread 函数这个花括号中的源代码, 理解这些源代码是如何改变线程状态的,并与 PspReadyThread 函数中的源代码进行比较,说明这两段源代码的异同,体会为什么在这里不能直接调用 PspReadyThread 函数。通过以上的调试,可以将线程由运行状态进入就绪状态的步骤总结如下:

(1) 线程中断运行,将线程中断运行时的上下文保存到线程控制块中。

(2) 如果处于运行状态的线程被更高优先级的线程抢先,就需要将该线程插入其优先级对应的就绪队列的队首。 (注意,如果处于运行状态的线程主动让出处理器,例如时间片用完,就需要将程插入其优先级对应的就绪队列的队尾。 )

(3) 将线程的状态由 Running 修改为 Ready。至此, loop 线程已经进入就绪状态了, 接下来调试控制台派遣线程会得到处理器进入运行状态的过程。

2.3: 线程由就绪状态进入运行状态

按照下面的步骤调试线程状态转换的过程:

1. 按 F5 继续执行, 在 PspUnreadyThread 函数中的断点处中断。 在快速监视对话框中查看 “*Thread”表达式的值。其中 State 域的值为 1(Ready ) ,双向链表项 StateListEntry 的 Next 和 Prev 指针的值都不为 0,说明这个线程处于就绪状态,并在优先级为 24 的就绪队列中;StartAddr 域的值为 IopConsoleDispatchThread , 说明这个线程就是控制台派遣线程。 仔细阅读 PspUnreadyThread 函数中的源代码,理解这些源代码是如何改变线程状态的。

2. 关闭快速监视对话框后,在“调用堆栈”窗口中激活 PspSelectNextThread 函数对应的堆栈项,可以看到在 PspSelectNextThread 函数中已经将 PspCurrentThread 全局指针指向了控制台派遣线程,并在调用 PspUnreadyThread 函数后,将当前线程的状态改成了 Running。

3. 在“调用堆栈”窗口中激活 PspUnreadyThread 函数对应的堆栈项,然后按 F10 单步调试,直到返回 PspSelectNextThread 函数并将线程状态修改为 Running 。再从快速监视对话框中查看“*PspCurrentThread”表达式的值,观察当前占用处理器的线程的情况。其中 State 域的值为 2(Running ) ,双向链

表项 StateListEntry 的 Next 和 Prev 指针的值都为 0,说明控制台派遣线程已经处于运行状态了。接下来,会将该线程的上下文从线程控制块(TCB )复制到处理器的各个寄存器中,处理器就可以从该线程上次停止运行的位置继续运行了。通过以上的调试,可以将线程由就绪状态进入运行状态的步骤总结如下:

(1) 将线程从其优先级对应的就绪队列中移除。

(2) 将线程的状态由 Ready 修改为 Zero。

(3) 将线程的状态由 Zero 修改为 Running。

(4) 将线程的上下文从线程控制块(TCB )复制到处理器的各个寄存器中,让线程从上次停止运行的位置继续运行。至此,控制台派遣线程已经开始运行了。因为此时没有比控制台派遣线程优先级更高的线程来抢占处理器,所以控制台派遣线程可以一直运行,直到将此次由于空格键被按下而产生的键盘事件处理完毕,然后控制台派遣线程会由运行状态重新进入阻塞状态,开始等待下一个键盘事件到来。

2.4,线程由运行状态进入阻塞状态:

按照下面的步骤调试线程状态转换的过程:

1. 按 F5 继续执行, 在 PspWait 函数中的断点处中断。 在快速监视对话框中查看 “*PspCurrentThread”

表达式的值,观察当前占用处理器的线程的情况。其中 State 域的值为 2(Running ) ,双向链表项 StateListEntry 的 Next 和 Prev 指针的值都为 0, 说明这个线程仍然处于运行状态; StartAddr 域的值为 IopConsoleDispatchThread ,说明这个线程就是控制台派遣线程。

2. 按 F10 单步调试,直到左侧的黄色箭头指向代码第 248 行。再从快速监视对话框中查看“*PspCurrentThread”表达式的值。其中 State 域的值为 3(Waiting ) ,双向链表项StateListEntry 的 Next 和 Prev 指针的值都不为 0,说明控制台派遣线程已经处于阻塞状态了,并在某个同步对象的等待队列中。第 248 行代码可以触发线程调度功能,会中断执行当前已经处于阻塞状态的控制台派遣线程,并将处理器上下文保存到该线程的线程控制块中。通过以上的调试,可以将线程由运行状态进入阻塞状态的步骤总结如下:

(1) 将线程插入等待队列的队尾。

(2) 将线程的状态由 Running 修改为 Waiting。

(3) 将线程中断执行,并将处理器上下文保存到该线程的线程控制块中。至此,控制台派遣线程已经进入阻塞状态了。因为此时 loop 线程是就绪队列中优先级最高的线程,线程调度功能会选择让 loop 线程继续执行。按照下面的步骤调试线程状态转换的过程:

1. 按 F5 继续执行,与本实验 3.2.3 节中的情况相同,只不过这次变为 loop 线程由就绪状态进入运行状态。

2. 再按 F5 继续执行,EOS 不会再被断点中断。激活虚拟机窗口,可以看到 loop 线程又开始不停的执行死循环了。

3. 可以再次按空格键,将以上的调试步骤重复一遍。这次调试的速度可以快一些,仔细体会线程状态转换的过程。

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

OS 已经实现了一个 suspend 命令, 其命令函数为

ConsoleCmdSuspendThread (在 ke/sysproc.c 文件的第 843 行) 。在这个命令中调用了 Suspend 原语(在 ps/psspnd.c 文件第 27 行的 PsSuspendThread 函数中实现) 。Suspend 原语可以将一个处于就绪状态的线程挂起。以 loop 线程为例,当使用 suspend 命令将

其挂起时,loop 线程的执行计数就会停止增长。按照下面的步骤观察 loop 线程被挂起的情况:

1. 删除之前添加的所有断点。

2. 按 F5 启动调试。

3. 待 EOS 启动完毕,在 EOS 控制台中输入命令“loop ”后按回车。此时可以看到 loop 线程的执行计数在不停增长,说明 loop 线程正在执行。记录下 loop 线程的 ID。

4. 按 Ctrl+F2 切换到控制台 2,输入命令“suspend 31” (如果 loop 线程的 ID 是 31)后按回车。

5. 按 Ctrl+1 切换回控制台 1,可以看到由于 loop 线程已经成功被挂起,其执行计数已经停止增长了。此时占用处理器的是 EOS 中的空闲线程。

四,实验收获及感悟:

通过本次实验,我学会了调试线程在各种状态间的转化过程,熟悉了线程的状态和转换,在实验中我通过为线程增加挂起状态,加深了对线程状态的理解。

五.实验截图:

实验5 进程的同步

一. 实验目的

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

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

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

二. 实验内容

1 准备实验

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

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

3.1 创建信号量

3.2 等待、释放信号量

3.2.1 等待信号量(不阻塞)

3.2.2 释放信号量(不唤醒)

3.2.3 等待信号量(阻塞)

3.2.4 释放信号量(唤醒)

4 修改EOS 的信号量算法

三. 实验结果

1. 按照下面的步骤查看生产者-消费者同步执行的过程:

(1)使用pc.c 文件中的源代码,替换之前创建的EOS 应用程序项目中EOSApp.c 文件内的源代码。

(2)按F7生成修改后的EOS 应用程序项目。

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

(4)在调试异常对话框中选择“否”,继续执行。

(5)立即激活虚拟机窗口查看生产者-消费者同步执行的过程

(6)待应用程序执行完毕后,结束此次调试。

2. 等待、释放信号量

(1)等待信号量(不阻塞)

生产者和消费者刚开始执行时,用来放产品的缓冲区都是空的,所以生产者在第一次调用WaitForSingleObject 函数等待Empty 信号量时,应该不需要阻塞就可以立即返回。

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

生产者线程通过等待Empty 信号量使空缓冲区数量减少了1,通过释放Full 信号量使满缓冲区数量增加了1,这样就表示生产者线程生产了一个产品并占用了一个缓冲区。

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

由于开始时生产者线程生产产品的速度较快,而消费者线程消费产品的速度较慢,所以当缓冲池中所有的缓冲区都被产品占用时,生产者在生产新的产品时就会被阻塞

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

只有当消费者线程从缓冲池中消费了一个产品,从而产生一个空缓冲区后,生产者线程才会被唤醒并继续生产14号产品。

四. 实验总结

今天的实验的内容是进程的同步,这也是进程这一概念提出后面临的一个非常大的问题,因为当进程在竞争临界资源时,由于进程的异步性,便会给系统造成混乱,所以能不能让诸多进程之间有效的共享资源和相互合作,就变成了一个很重要的问题。今天的实验也让我们直接的观察到了EOS 操作系统如何通过信号量机制解决进程同步问题,并对程序进行修改,这对我们今后的学习有很大的帮助,但对于解决大量同步操作带来的系统死锁问题,我还是欠缺实践和认识。

五. 实验截图

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

一,实验目的:

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

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

二,预备知识:

阅读本书第 6 章。重点阅读第 6.3 节和第 6.6 节,了解物理存储器的管理方式和进程逻辑地址空间的管理方式。

三,主要实验内容及步骤:

1,准备实验;

2,阅读控制台命令“ pm ”相关的源代码,并查看其执行的结果; 3,分配物理页和释放物理页:

接下来,在 pm 命令函数中添加分配物理页和释放物理页的代码,单步调试管理物理页的方法。按照

下面的步骤修改 pm 命令的源代码:

1. 使用 OS Lab 打开本实验文件夹中的 pm.c 文件(将文件拖动到 OS Lab 窗口中释放即可打开) 。此文件中有一个修改后的 ConsoleCmdPhysicalMemory 函数,主要是在原有代码的后面增加了分配物理页和释放物理页的代码。

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

3. 按 F7 生成修改后的 EOS Kernel 项目。

4. 按 F5 启动调试。

5. 待 EOS 启动完毕,在 EOS 控制台中输入命令“pm ”后按回车。

按照下面的步骤调试分配物理页和释放物理页的过程:

1. 结束之前的调试。

2. 在 ke/sysproc.c 文件的 ConsoleCmdPhysicalMemory 函数中,在调用 MiAllocateAnyPages 函数的代码行(第 1103 行)添加一个断点,在调用 MiFreePages 函数的代码行(第 1115 行)添加一个断点。

3. 按 F5 启动调试。

4. 待 EOS 启动完毕,在 EOS 控制台中输入命令“pm ”后按回车。

5. pm 命令开始执行后,会在调用 MiAllocateAnyPages 函数的代码行处中断,按 F11 调试进入MiAllocateAnyPages 函数。

6. 按 F10 单步调试 MiAllocateAnyPages 函数的执行过程,尝试回答下面的问题:

(1) 本次分配的物理页的数量是多少?分配的物理页的页框号是多少?

(2) 物理页是从空闲页链表中分配的?还是从零页链表中分配的?

(3) 哪一行语句减少了空闲页的数量?哪一行语句将刚刚分配的物理页由空闲状态修改为忙状态?

(4) 绘制 MiAllocateAnyPages 函数的流程图。

继续调试释放物理页的过程:

1. 按 F5 继续执行,会在调用 MiFreePages 函数的代码行处中断,按 F11 调试进入 MiFreePages 函数。

2. 按 F10 单步调试 MiFreePages 函数的执行过程,尝试回答下面的问题:

(1) 本次释放的物理页的数量是多少?释放的物理页的页框号是多少?释放的物理页是之前分配的物理页吗?

(2) 释放的物理页是被放入了空闲页链表中?还是零页链表中?

(3) 绘制 MiFreePages 函数的流程图。

结束此次调试。 继续修改 pm 命令的源代码, 尝试在调用 MiAllocateAnyPages 函数时分配多个物理页,然后在调用 MiFreePages 函数时将分配的多个物理页释放,并练习调试这两个函数在分配多个物理页和释放多个物理页时执行的过程

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

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

接下来, 在 vm 命令函数中添加分配虚拟页和释放虚拟页的代码, 单步调试管理虚拟页的方法。 首先,按照下面的步骤修改 vm 命令的源代码:

1. 使用 OS Lab 打开本实验文件夹中的 vm.c 文件(将文件拖动到 OS Lab 窗口中释放即可打开) 。此文件中有一个修改后的 ConsoleCmdVM 函数,主要是在原有代码的后面增加了分配虚拟页和释放物理页的代码。

2. 使用 vm.c 文件中 ConsoleCmdVM 函数的函数体替换 ke/sysproc.c 文件中 ConsoleCmdVM 函数的函数体。

3. 按 F7 生成修改后的 EOS Kernel 项目。

4. 按 F5 启动调试。

5. 待 EOS 启动完毕,在 EOS 控制台中输入命令“vm 1”后按回车。

命令执行的结果会同时转储在“输出”窗口中。尝试说明分配虚拟页或者释放虚拟页后虚拟地址描述符以及物理存储器的变化情况。

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

四,实验收获及感悟:

通过本次实验,让我学会了通过查看物理存储器的使用情况,并练习分配和回收物理内存,从而掌握物理存储器的管理方法。以及通过查看进程逻辑地址空间的使用情况,并练习分配和回收虚拟内存,从而掌握进程逻辑地址空间的管理方法。

五:实验截图:

实验8:分页存储器的管理

一,实验目的:

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

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

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

二,预备知识:

阅读本书第 6 章。了解 i386 处理器的二级页表硬件机制,EOS 操作系统的分页存储器管理方式,以及进程地址空间的内存分布。

三,主要实验内容及步骤:

1,准备实验;

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

使用 OS Lab 打开本实验文件夹中的 memory.c 和 getcr3.asm 文件(将文件拖动到 OS Lab 窗口中释放即可打开) 。仔细阅读这两个文件中的源代码和注释,main 函数的流程图可以参见图 16-1。

按照下面的步骤查看 EOS 应用程序进程的页目录和页表:

1. 使用 memory.c 文件中的源代码替换之前创建的 EOS 应用程序项目中 EOSApp.c 文件中的源代码。

2. 右键点击“项目管理器”窗口中的“源文件”文件夹节点,在弹出的快捷菜单中选择“添加”中的“添加新文件”。

3. 在弹出的“添加新文件”对话框中选择“asm 源文件”模板。

4. 在“名称”中输入文件名称“func ”。

5. 点击“添加”按钮添加并自动打开文件 func.asm。

6. 将 getcr3.asm 文件中的源代码复制到 func.asm 文件中。

7. 按 F7 生成修改后的 EOS 应用程序项目。

8. 按 F5 启动调试。

9. 应用程序执行的过程中,会将该进程的二级页表映射信息输出到虚拟机窗口和 OS Lab“输出”窗口中,输出内容如图 16-2(a ) 。

10. 将“输出”窗口中的内容复制到一个文本文件中。

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

需要对 EOS 应用程序进行一些修改:

1. 结束之前的调试。

2. 取消 EOSApp.c 第 121 行语句的注释(该行语句会等待 10 秒) 。

3. 按 F7 生成修改后的 EOS 应用程序项目。

4. 按 F5 启动调试。

5. 在“Console-1”中会自动执行 EOSApp.exe,创建该应用程序进程。利用其等待 10 秒的时间,按 Ctrl+F2 切换到“Console-2” 。

6. 在“Console-2”中输入命令“mm ”后按回车,会将系统进程的二级页表映射信息输出到虚拟机窗口和 OS Lab 的“输出”窗口,输出内容如图 16-2(b ) 。注意,在图 16-2(b )中添加了一些空行,方便与图 16-2(a )比较。 “Console-1”中的应用程序在等待 10 秒后,又会输出和图 16-2(a )一致的内容。

7. 将“输出”窗口中的内容复制到一个文本文件中。

控制台命令“mm ”对应的源代码在 EOS 内核项目 ke/sysproc.c 文件的 ConsoleCmdMemoryMap 函数中(第 382 行) 。阅读这部分源代码后会发现,其与 EOSApp.c 文件中的源代码基本类似。

结合图 16-2(a )和(b )回答下面的问题:

EOS 启动后系统进程是一直运行的, 所以当创建应用程序进程后, 系统中就同时存在了两个进程,这两个进程是否有各自的页目录?在页目录映射的页表中,哪些是独占的,哪些是共享的?分析其中的原因。

统计当应用程序进程和系统进程并发时,总共有多少物理页被占用?

应用程序结束后,在“Console-1”中再次输入命令“mm ” ,查看在没有应用程序进程时,系统进程的页目录和页表。将“输出”窗口中的内容复制到一个文本文件中。将输出的内容与图 16-2(b )比较,思考为什么系统进程(即内核地址空间)占用的物理页会减少?(提示:创建应用程序进程时,EOS 内核要 为其创建 PCB,应用程序结束时,内核要释放 PCB 占用的内存。 ) 4, 查看应用程序进程并发时的页目录和页表

需要对 EOS 应用程序进行一些修改:

1. 结束之前的调试。

2. 取消 EOSApp.c 第 201 行语句的注释(该行语句会等待 10 秒) 。

3. 按 F7 生成修改后的 EOS 应用程序项目。

4. 按 F5 启动调试。

5. 在“Console-1”中会自动执行 EOSApp.exe,创建该应用程序进程。利用其等待 10 秒的时间,按Ctrl+F2 切换到“Console-2” 。

6. 在“Console-2”中输入“eosapp ”后按回车,再执行一个 EOSApp.exe。

7. 由 EOSApp.exe 创建的两个并发进程会先后在各自的控制台和 OS Lab“输出”窗口中,输出各自的二级页表映射信息。输出的内容如图 16-3。

8. 将“输出”窗口中的内容复制到一个文本文件中。

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

下面通过编程的方式,从 EOS 操作系统内核中申请两个未用的物理页,将第一个物理页当作页表,映射基址为 0xE0000000 的 4M 虚拟地址空间,然后将第二个物理页分别映射到基址为 0xE0000000 和0xE0001000 的 4K 虚拟地址空间。从而验证下面的结论:

虽然进程可以访问 4G 虚拟地址空间,但是只有当一个虚拟地址通过二级页表映射关系能够映射到实际的物理地址时,该虚拟地址才能够被访问,否则会触发异常。

所有未用的物理页都是由 EOS 操作系统内核统一管理的,使用时必须向内核申请。

为虚拟地址映射物理页时,必须首先为页目录安装页表,然后再为页表安装物理页。并且只有在刷新快表后,对页目录和页表的更改才能生效。 不同的虚拟地址可以映射相同的物理页,从而实现共享。

四,实验收获及感悟:

通过本次实验,让我学会了查看 EOS 应用程序进程和系统进程的二级页表映射信息,理解页目录和页表的管理方式。并且学会了编程修改页目录和页表的映射关系,理解了分页地址变换原理。

五:实验截图:

实验11:扫描 FAT12 文件系统管理的软盘

一,实验目的:

1,通过查看 FAT12 文件系统的扫描数据,并调试扫描的过程,理解 FAT12 文件系统管理软盘的方式。

2,通过改进 FAT12 文件系统的扫描功能,加深对 FAT12 文件系统的理解。

二,预备知识:

阅读本书第 8 章,学习 FAT12 文件系统技术细节。关于 FAT 文件系统更详细的信息,还可以参阅微软硬件白皮书《FAT: General Overview of On-Disk Format 》 (在 OS Lab 的“帮助”菜单中选择“其它帮助文档”中的“FAT 文件系统概述” ) 。

三,主要实验内容及步骤:

1,准备实验;

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

阅读 ke/sysproc.c 文件中第 1321 行的 ConsoleCmdScanDisk 函数,学习“sd ”命令是如何扫描软盘上的 FAT12 文件系统的。在阅读的过程中需要注意下面几点:

在开始扫描软盘之前要关闭中断,之后要打开中断,这样可以防止在命令执行的过程中有其它线程修改软盘上的数据。

以软盘的盘符“A:”做为 ObpLookupObjectByName 函数的参数,就可以获得 FAT12 文件系统设备对象的指针。

FAT12 文件系统设备对象的扩展块(FatDevice->DeviceExtension)是一个卷控制块(VCB ,在文件 io/driver/fat12.h 的第 115 行定义) ,从其中可以获得文件系统的重要参数,并可以扫描 FAT表。

FatGetFatEntryValue 函数可以根据第二个参数所指定的簇号,返回簇在 FAT 表中对应项的值,在扫描 FAT 表时通过调用此函数来统计空闲簇的数量(FreeClusterCount ) 。

按照下面的步骤执行控制台命令“sd ” ,查看扫描的结果:

1. 按 F7 生成在本实验 3.1 中创建的 EOS Kernel 项目。

2. 按 F5 启动调试。

3. 待 EOS 启动完毕,在 EOS 控制台中输入命令“sd ”后按回车。

观察命令执行的结果,如图 19-1 所示,可以了解 FAT12 文件系统的信息。

3,根据 BPB 中的信息计算出其他信息

修改“sd ”命令函数 ConsoleCmdScanDisk 的源代码,在输出 BPB 中保存的信息后,不再通过pVcb->FirstRootDirSector 等变量的值进行打印输出,而是通过 BPB 中保存的信息重新计算出下列信息,并打印输出:

(1) 计算并打印输出根目录的起始扇区号,即 pVcb->FirstRootDirSector 的值。

(2) 计算并打印输出根目录的大小,即 pVcb->RootDirSize 的值。

(3) 计算并打印输出数据区的起始扇区号,即 pVcb->FirstDataSector 的值。

(4) 计算并打印输出数据区中簇的数量,即 pVcb->NumberOfClusters 的值。

4, 阅读控制台命令“ dir ”相关的源代码,并查看其执行结果; 按照下面的步骤执行控制台命令“dir ” ,查看扫描的结果:

1. 按 F7 生成在本实验 3.1 中创建的 EOS Kernel 项目。

2. 按 F5 启动调试。

3. 待 EOS 启动完毕,在 EOS 控制台中输入命令“dir ”后按回车。 观察命令执行的结果,可以看到当前软盘中存储的文件的信息。

5,输出每个文件所占用的磁盘空间的大小

修改 “dir ” 命令函数 ConsoleCmdDir 的源代码, 要求在输出每个文件的名称、 大小、 最后改写时间后,再输出每个文件所占用的磁盘空间(以字节为单位) 。

四,实验收获及感悟:

通过本次实验,让我学会了如何通过查看 FAT12 文件系统的扫描数据,并调试扫描的过程,理解 FAT12 文件系统管理软盘的方式。以及通过改进 FAT12 文件系统的扫描功能,加深了对 FAT12 文件系统的理解。

五:实验截图:


相关文章

  • 进出口业务情景模拟实验报告该
  • 内蒙古工业大学进出口业务情景模拟实验报告目实验目的录„„„„„„„„„„„„„„„„„„„„„„„„„„„„„ 1 „„„„„„„„„„„„„„„„„„„„„„„ 2 „„„„„„„„„„„„„„„„„„„„„„„„„2 „„„„„„„„„ ...查看


  • 大学生计算机基础实验报告
  • < 大学计算机基础>课程 实验报告手册 学院 年级 专业 姓名 学号 任课教师 上机地点 (以上由学生填写) 实验教师(签字) 西南大学计算机与信息科学学院 计算机基础教育系 年 月 日 一. 实验说明 本课程实验分为一般性实验 ...查看


  • 大学物理实验论文-完整版[1]-好[1]
  • 大学物理实验论文 标题:物理实验的感悟与体会 摘要:在本学期的实验课中,我感悟和体会很多,让我学到许多平时学习不到的大学.虽然在很多的物理实验中,我们只是在复现课堂上所学的理论知识原理与效果,但因为物理实验有着诸多不同的因素,要求我们必须端 ...查看


  • 实验教学质量评价与指标体系建立
  • 第30卷第2期 唐山师范学院学报 2008年3月 Vol.30 No.2 Journal of Tangshan Teachers College Mar. 2008 实验教学质量评价与指标体系建立 史智平 (宝鸡文理学院 物理系,陕西 宝 ...查看


  • 实验室管理部分规章制度目录
  • 实验室管理部分规章制度目录 1. 江西理工大学实验教学管理办法 2. 江西理工大学实验课考试管理办法 3. 江西理工大学实验教学试讲制度 4. 江西理工大学实验室工作条例 5. 江西理工大学实验室规则 6. 江西理工大学学生实验守则 7.  ...查看


  • 小型超市销售管理系统
  • 小型超市销售管理系统 实 验 报 告 实验课程: 信息系统分析与设计 学生姓名:曹志明 冯建明 冯伟 胡华 赖玉峰 专业班级: 信息管理041班 组 长:曹志明(6104104002) 小组成员:冯建明(6104104027) 冯 伟(61 ...查看


  • 大学计算机学习实验报告格式
  • <大学计算机基础>实验报告格式 要求使用 黑色钢笔/签字笔 笔迹工整,尽量不涂抹! 手写! 实验心得不要雷同,应该有自己的见解!通过写实验报告理论联系实际,每次实验都要有收获! 封面时间: 2011 年 10 月 8 日 实验报 ...查看


  • 大连理工大学操作系统实验报告
  • 大连理工大学 本科实验报告 课程名称:操作系统实验 学院(系):计算机科学与技术学院 专 班 学业:计算机科学与技术级:号:电计 学生姓名: 年月日 实验项目列表序号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...查看


  • 机能学实验报告中的问题分析
  • ・408・ 山西医科大学学报:基础医学教育版,2010年4月,12(4) 机能学实验报告中的问题分析 芜湖 241002)朱海龙, 张根葆 (皖南医学院病理生理学教研室, 摘要: 机能学实验报告是培养医学生科研写作能力的重要途径,,并在此基 ...查看


  • 大学物理开放实验网络选课系统的优化设计
  • 开发案例 大学物理开放实验网络选课系统的优化设计 李丽霞 , 王雪凤 (广东商学院华商学院信息工程系,增城511300) 摘 要:以大学物理开放实验课程为例,针对当前高校物理开放性实验网络选课系统在辅助教学方 面的一些不足,提出改进措施,设 ...查看


热门内容