一、 实验目的
·练习使用 EOS API 函数 CreateProcess 创建一个进程,掌握创建进程的方法,理解进程和程序的区别。
·调试跟踪 CreateProcess 函数的执行过程,了解进程的创建过程,理解进程是资源分配的基本单位。
·调试跟踪 CreateThread 函数的执行过程,了解线程的创建过程,理解线程是调度的基本单位。 二、 实验内容
1、 执行了实验指导书3.2的步骤,学习到了如何使用控制台命令创建一个EOS 应用程序的进程,结果如下图所示。
2、 执行了实验指导书3.3的步骤,学习到了如何使用控制台命令创建一个EOS 应用程序的进程,结果如下图所示。
3、 执行了实验指导书3.4的步骤,观察到了系统中有三个进程,其中第一个 是系统进程,镜像名称为空;另外两个是刚刚创建的应用程序的进程,镜像名称分别为“A:\\EOSApp.exe”和“A:\\Hello.exe”。其中,A:\\EOSApp.exe进程是父进程,其主线程处于运行状态;A:\\Hello.exe 进程是子进程,其主线程处于就绪状态,当父进程的主线程通过调用WaitForSingleObject 函数进入阻塞状态让出处理器后,这个处于就绪状态的线程就会占用处理器开始运行。这两个应用程序的进程和线程的优先级都为 8。说明了处于阻塞队列的进程等到I/O完成后按照优先级策略排成就绪队列,等到获得CPU后便可立即执行。
4、 执行了实验指导书3.5的步骤,观察到了用于存储内核管理的数据,已经使用的虚拟地址空间都大于 0x80000000,说明了内核在 4G 虚拟地址空间;观察到了CreateProcess 函数中所有指令的虚拟地址都是大于 0x80000000 的,验证了内核程序(kernel.dll)位于高 2G 虚拟地址空间;观察到了“反汇编”窗口中main 函数所有指令的虚拟地址都是小于 0x80000000 的,说明应用程序(eosapp.exe)位于低 2G 的虚拟地址空间中;观察到了调试PspCreateProcessEnvironment 函数时*NewProcess 的值的变化情况,了解了各个成员变量的初始化情况;用 NewTwoProc.c 文件中的源代码替换 EOS 应用程序项目中 EOSApp.c 文件内的源代码,生成后启动调试,查看多个进程并发执行的结果如图所示,了解了通过编程的方式创建一个应用程序的多个进程。
5、 执行了实验指导书3.6的步骤,观察到了在应用程序进程的线程列表中,包含两个线程,一个是应用程序的主线程,处于阻塞状态,说明其正在等待工作线程结束;另一个是在应用程序中通过调用 CreateThread 函数创建的工作线程处于运行状态,说明其正在执行线程函数并命中了断点。观察到了阻塞在线程上的线程链表中显示了应用程序的主线程阻塞在该工作线程上,说明了主线程在等待该工作线程结束。
6、 执行了实验指导书3.7的步骤,观察到了当前系统中,只创建了一个线程,命中断点后创建一个新的系统线程,并使用该新建线程继续进行操作系统的初 始化工作。此时第一个系统线程使自己的优先级降为 0,退化为空闲线程,让 刚刚新建的还处于就绪状态的系统初始化线程抢占处理器开始运行。观察到了“就绪线程队列”窗口,优先级为 0 的空闲线程,以及优先级为 24 的控制台派遣线程和四个控制台线程分别在其优先级对应的就绪队列中,说明了核心调度器管理活动进程的主要数据结构是就绪队列,每个CPU都有自己的就绪队列,而每个活动进程某一时刻只会在一个就绪队列中;观察到了“线程运行轨迹”窗口中系统线程的状态转换过程和运行轨迹,说明三种基本状态互相转换。一个进程在运行期间,不断地从一种状态转换到另一种状态,它可以多次处于就绪状态和执行状态,也可以多次处于阻塞状态。
(1) 就绪→执行:处于就绪状态的进程,当进程调度程序为之分配了处理机后,该进程便由就绪状态转变成执行状态。
(2) 执行→就绪:处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从执行状态转变成就绪状态。 (3) 执行→阻塞:正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成阻塞状态。
(4) 阻塞→就绪:处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。
7、 针对实验指导书3.5中提出的问题,进行以下尝试和回答:
尝试根据之前对 PsCreateProcess 函数和 PspCreateProcessEnvironment 函数执行过程的跟踪调试,绘制一幅进程创建过程的流程图。
三、 思考练习
1. 在源代码文件NewTwoProc.c 提供的源代码基础上进行修改,要求使用hello.exe 同时创建10 个进程。
提示:可以使用 PROCESS_INFORMATION 类型定义一个有 10 个元素的数组,每一个元素对应一个进程。使用一个循环创建 10 个子进程,然后再使用一个循环等待 10 个子进程结束,得到退出码后关闭句柄。
答:创建10进程代码如下: #define PROC_COUNT 10 int main(int argc, char* argv[]) {
结束 创建线程 为进程分配句柄 创建进程控制块 创建进程的地址空间 开始 将所需的地址映像加载到进程的地址空间 // // 启动调试 EOS 应用程序前要特别注意下面的问题: // // 1、如果要在调试应用程序时能够调试进入内核并显示对应的源码, // 必须使用 EOS 核心项目编译生成完全版本的 SDK 文件夹,然 // 后使用此文件夹覆盖应用程序项目中的 SDK 文件夹,并且 EOS // 核心项目在磁盘上的位置不能改变。 // STARTUPINFO StartupInfo; // 子进程的启动信息 PROCESS_INFORMATION ProcInfoArray[PROC_COUNT]; // 子进程信息数组,包含元素的数量与创建子进程的数量相同 ULONG ulExitCode; // 子进程的退出码 INT i; // 循环计数器 printf(\"Create %d processes and wait for the processes exit...\\n\\n\PROC_COUNT); // // 使子进程和父进程使用相同的标准句柄。 // StartupInfo.StdInput = GetStdHandle(STD_INPUT_HANDLE); StartupInfo.StdOutput = GetStdHandle(STD_OUTPUT_HANDLE); StartupInfo.StdError = GetStdHandle(STD_ERROR_HANDLE); // // 为一个应用程序同时创建多个子进程。 for(i=0; i 3. 在 PsCreateProcess 函 数 中 调 用 了 PspCreateProcessEnvironment 函 数 后 又 先 后 调 用 了PspLoadProcessImage 和 PspCreateThread 函数,学习这些函数的主要功能。能够交换这些函数被调用的顺序吗?思考其中的原因。 答:PspCreateProcessEnvironment函数的主要功能是创建进程控制块并且为进程创建了地址空间和分配了句柄表,PspLoadProcessImage函数的主要功能是将进程的可执行映像加载到了进程的地址空间中,PspCreateThread函数的主要功能 是创建了进程的主线程。加载可执行映像之前必须已经为进程创建了地址空间,这样才能够确定可执行映像可以被加载到内存的什么位置;在创建主线程之前必须已经加载了可执行映像,这样主线程才能够知道自己要从哪里开始执行,执行哪些指令。由此可得这三个函数被调用的顺序是不能够改变的,因而不能交换他们的顺序。 因篇幅问题不能全部显示,请点此查看更多更全内容