执行一个程序,必然就产生一个进程(process)。最直接的程序执行方式就是在shell (如
Win95 的文件总管或Windows 3.x 的文件管理员)中以鼠标双击某一个可执行文件图标
(假设其为App.exe),执行起来的App 进程其实是shell 调用CreateProcess 激活的。
让我们看看整个流程:
1. shell 调用
CreateProcess
激活
App.exe
。
Shell即命令解释器,是操作系统引导时即加载的一个系统进程,在Windows任务管理器里面可以看到一个名为”Explorer.exe”的进程,就是它了。
2. 系统产生一个「
进程核心对象
」,计数值为1。
而系统为该进程创建4GB的虚拟地址空间(在Win2000/WinXP下,每个进程可以有2GB的私有地址空间,剩余的2GB由操作系统占用)用来加载App.exe和其他必要的DLL函数;
3. 系统为此进程建立一个4GB 地址空间。
4. 加载器将必要的码加载到上述地址空间中,包括App.exe 的程序、资料,以及
所需的动态联结函数库(DLLs)。加载器如何知道要加载哪些DLLs 呢?它
们被记录在可执行文件(PE 文件格式)的.idata section 中。
5. 系统为此进程建立一个执行线程,称为
主执行线程
(primary thread)。执行线程才是
CPU 时间的分配对象。
6. 系统调用C runtime 函数库的
Startup code
。(
C/C++ run-time startup function
。
)
c/c++运行时库有四个版本的启动函数,他们分别对应不同类型的应用程序。比如,需要ANSI字符和字符串的GUI应用程序的启动函数是WinMainCRTStartup,其对应的进入点函数是WinMain,需要Unicode字符和字符串的GUI应用程序的启动函数是wWinMainCRTStartup,其对应
的进入点函数是wWinMain,而需要ANSI字符和字符串的CUI应用程序(如控制台console程序)的应用程序的启动函数是mainCRTStartup,对应的入口点函数为main;需要Unicode字符和字符串的CUI应用程序(如控制台console程序)的应用程序的启动函数为wmainCRTStartup,对应的入口点函数为wmain;
启动函数的功能可概括如下:
l 检索指向新进程的完整命令行的指针
l 检索指向新进程的环境变量的指针
l 对C/C++ run time's global variables进行初始化。如果包含了stdlib.h头文件,你的代码就能访问这些变量。详见下表:
l 对C运行期内存分配函数(mallco和calloc)以及low-level input/output routines使用的heap(堆)进行初始化
l 为所有的全局和静态C++类对象调用构造函数
当上述这些初始化操作完成后,C/C++ run-time startup function就调用应用程序的进入点函数。
7. Startup code 调用App 程序的
WinMain
函数。
如果编写了一个WinMain函数,它将以如下的形式被调用:
GetStartupInfo( &StartupInfo );
int nMainRetVal = WinMain( GetModuleHandle(NULL), NULL, pszCommandLineAnsi,
(StartupInfo.dwFlags & STARTF_USESHOWWINDOW)
? StartupInfo.wShowWindow : SW_SHOWDEFAULT );
8. App 程序开始运作。
9. 使用者关闭App 主窗口,使WinMain 中的消息循环结束掉,于是WinMain 结束。
10. 回到Startup code。
当进入点函数返回时,启动函数便调用C运行期的exit函数,将返回值(nMainRetVal)传递给它。
Exit函数负责如下操作:
l 调用由_onexit函数的调用而注册的任何函数
l 为所有全局的和静态的C++类对象调用析构函数
l 调用操作系统的ExitProcess函数,将nMainRetVal传递给它。这使得操作系统能够撤销此进程并设置它的exit code(该代码保存在该进程对应的内核对象中)。
11. 回到系统,系统调用ExitProcess 结束进程。
运行库在调用用户程序的main或WinMain函数之前,进行了一些初始化工作。初始化完成后,接着才调用了我们自己编写的main或WinMain函数。这样, C/C++运行时库和应用程序就正常地工作起来了。
当用户程序的main或WinMain函数执行结束后,返回值被当做参数传入exit函数中,有exit完成程序执行的收尾工作,包括析构C++全局和静态类,调用操作系统的ExitProcess函数,告知进程退出等。