实验三 进程的创建
一、 实验目的
\1. 练习使用 EOS API 函数 CreateProcess 创建一个进程,掌握创建进程的方法,理解进程和程序的区别。
\2. 调试跟踪 CreateProcess 函数的执行过程,了解进程的创建过程,理解进程是资源分配的基本单位。
\3. 调试跟踪 CreateThread 函数的执行过程,了解线程的创建过程,理解线程是调度的基本单位。
二、 实验内容
1、 执行了准备试验的步骤,学到了如何构建EOS的kernel,进而生成并使用SDK。
2、 执行了练习使用控制台命令创建一个 EOS 应用程序的进程的步骤,学会了如何编辑软盘文件和运行应用程序。
3、 执行了练习通过编程的方式让应用程序创建另一个应用程序的进程的步骤,学会了可以在一个应用程序中通过调用CreateProcess来创建新的进程,并使用WaitForSingleObject来等待进程止执行完毕。
4、 执行了从应用程序的角度理解进程的创建过程的步骤,学会了如何调试应用程序,可以同过断点的方式来使应用程序停下来,进而观察每一行代码的执行情况。
当命中第54行的代码时,用于新建进程的代码已经执行完毕。此时,在进程列表中就可以观察到新的进程,并且该进程处于就绪状态,但是不能立即执行。
当主程序调用WaitForSingleObject后,就会让出处理器,新建进程就会得到运行机会。于是产生了在主程序等待之前控制台没有输出,当主程序等待后,新建进程得到执行机会后,控制台就开始输出。
从这个现象可以总结出,新建的进程处于就绪队列中,但是并不会立即执行,当得到运行机会,如当前运行中的进程放弃处理器时,新建进程才能运行。
5、执行了从内核的角度理解进程的创建过程的步骤,掌握了进程的创建过程。当用户程序调用创建进程的API时,会跳转到内核空间进行创建进程的流程,创建流程如下:
l CreateProccess
l PsCreateProcess
l PspCreateProcessEnvironment
n ObCreateObject
n MmCreateProcessAddressSpace
n 进行其他进程控制块的初始化工作(优先级等)
l 装载可执行映像
l 创建主线程
用户程序调用系统提供的CreateProcess函数,由这个API来调用内核的PsCreateProcess函数,在PspCreateProces函数中开始创建进程,首先调用了PspCreateProcessEnvironment来创建进程的执行环境,在此函数中创建了进程控制块、为进程分配了内存空间;然后PspCreateProcess继续装载可执行映像、创建主线程等操作,最后完成了进程创建的全部操作。
6、 执行了练习通过编程的方式创建一个应用程序的多个进程的步骤。
1、 执行了在应用程序进程中创建一个工作线程的步骤。
创建工作进程只需调用EOS系统封装的内核接口即可,在调用时需要将线程函数的入口地址传递给API。
AppThreadHandle = CreateThread( 0, // 默认堆栈大小
AppThread, // 函数入口地址
NULL, // 线程函数参数
0, // 创建标志
NULL ); // 线程 ID
2、 执行了系统线程的创建过程的步骤。
在EOS操作系统的启动过程中,会先建立一个系统进程,并进一步创建系统线程,该线程会初始化系统运行需要的各种环境。该线程先创建了一个新的线程用于进一步初始化系统,然后又创建了控制台派遣线程,然后又穿件了四个控制台线程用于四个虚拟终端,并且控制台线程会因为等待键盘事件而进入阻塞状态。
三、 思考练习
- 在源代码文件NewTwoProc.c 提供的源代码基础上进行修改,要求使用hello.exe 同时创建 10 个进程。 提示:可以使用 PROCESS_INFORMATION 类型定义一个有 10 个元素的数组,每一个元素对应一个进程。 使用一个循环创建 10 个子进程,然后再使用一个循环等待 10 个子进程结束,得到退出码后关闭句柄。
答:由给出的代码和上面的结论可以知道,只要先初始化一个创建进程时用到的初始化信息,并创建用于存放进程信息的数组,然后再循环创建进程即可,下面给出了关键代码。
STARTUPINFO StartupInfo;
PROCESS_INFORMATION[10] ProcInfos;
ULONG ulExitCode[10];
INT nResult = 0; // 函数返回值。0 表示成功,非 0 表示失败。
int i;
printf(“Create ten processes and wait for the processes exit…\n\n”);
//
// 使子进程和父进程使用相同的标准句柄。
//
StartupInfo.StdInput = GetStdHandle(STD_INPUT_HANDLE);
StartupInfo.StdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
StartupInfo.StdError = GetStdHandle(STD_ERROR_HANDLE);
//
// 为一个应用程序同时创建十个子进程。
//
for (i = 0; i < 10; ++i)
{
CreateProcess(“A:\Hello.exe”, NULL, 0, &StartupInfo, &ProcInfo[i]);
}
for (i = 0; i < 10; ++i)
{
WaitForSingleObject(ProcInfo[i].ProcessHandle, INFINITE);
}
for (i = 0; i < 10; ++i)
{
GetExitCodeProcess(ProcInfo[i].ProcessHandle, &ulExitCode[i]);
printf("\nThe process %d exit with %d.\n", i, ulExitCode[i]);
CloseHandle(ProcInfo[i].ProcessHandle);
CloseHandle(ProcInfo[i].ThreadHandle);
}
- 在 PsCreateProcess 函数中调用了 PspCreateProcessEnvironment 函数后又先后调用了 PspLoadProcessImage 和 PspCreateThread 函数,学习这些函数的主要功能。能够交换这些函数被调 用的顺序吗?思考其中的原因。
答:不能交换调用的顺序,在PspCreateProcessEnvironment函数中,为进程创建了进程控制块,用于记录进程运行时的各种信息,然后调用PSPLoadProcessImage函数来加载创建线程所需要的程序镜像,在没有加载程序镜像之前,是无法创建线程的。
调 用的顺序吗?思考其中的原因。
答:不能交换调用的顺序,在PspCreateProcessEnvironment函数中,为进程创建了进程控制块,用于记录进程运行时的各种信息,然后调用PSPLoadProcessImage函数来加载创建线程所需要的程序镜像,在没有加载程序镜像之前,是无法创建线程的。