《Windows核心编程系列》进程的建立和终止
作者:网络转载 发布时间:[ 2012/10/9 10:52:17 ] 推荐标签:
首先要给大家介绍的是进程内核对象。进程大家都不陌生,它是资源和分配的基本单位,而进程内核对象是与进程相关联的一个数据结构。操作系统内核通过它管理进程,也是操作系统原理上介绍的进程控制块(PCB)。举个例子,它相当于每个学生都有的学籍,学校管理我们都是通过学籍,什么记过了,处分了,开除学籍了,都是在学籍上做文章。
进程一般被定义为一个正在运行的程序的一个实例,它由两部分组成:
1:内核对象,操作系统用它来管理进程。内核对象也是系统保存进程统计信息的地方。
2:一个地址空间,其中包含所有可执行文件或DLL模块的代码和数据。
Windows支持两种类型的应用程序:GUI程序和CUI程序。前者是我们经常接触的,具有窗口外观的窗口应用程序。后者是控制台应用程序。在使用vc来开发应用程序时,会设置各种链接器开关。链接器根据这些开关将子系统的正确类型嵌入终生成的可执行文件。对于CUI程序这个开关是/SUBSYSTEM:CONSOLE。对于GUI程序,则是/SUBSYSTEM:WINDOWS。
这些开关会告诉链接器在链接时链接什么入口函数。对于GUI程序它的入口点函数时WinMain,CUI程序是main。
有人以为入口函数是程序执行的开始,其实这是不正确的。在入口点函数之前还有一个被称为启动函数的函数。该函数用来初始化C/C++运行库、构造全局和静态的C++对象等。
根据应用程序类型的不同,启动函数也不一样。ANSI字符集下,GUI程序的启动函数是WinMainCRTStartup,入口函数是WinMain。CUI的启动函数是mainCRTStartup,入口函数是main。Unicode字符集下,GUI程序的启动函数是wWinMainCRTStartup,入口函数是wWinMain,CUI的启动函数是wmainCRTStartup,入口点函数时wmain。
我们在写控制台下的应用程序时,可以通过argv来引用命令行参数,当时也很疑惑,为什么可以直接用呢?原来都是启动函数的功劳。它会在进入入口函数之前帮我们做其他工作:
1:获取命令行指针。
2:获取指向环境变量的指针
3:初始化C/C++运行库的全局变量。
4:初始化C运行库内存分配函数。
5:调用所有全局和静态C++类对象的构造函数。
完成所有这些工作后,启动函数会调用应用程序的入口点函数。入口点函数返回后启动函数获得入口点函数返回值,并将其传递给C运行库函数exit。Exit函数将调用所有全局和静态C++类对象的析构函数和其他清理工作。然后将入口函数的返回值传递给ExitProcess函数,结束进程并设置返回值为退出代码。
加载到进程地址空间的可执行文件或是DLL都有一个实例句柄。用以标识它在进程地址空间的位置。可执行文件的实例句柄被当做WinMain函数的第一个参数传入。它实际上是一个内存基地址。系统将可执行文件的映像加载到进程地址空间中的这个位置。映像加载到哪个地址是由链接器决定的。不同的链接器使用不同的首先基地址。exe文件和dll都会有一个默认的基地址。exe文件是400000,dll是10000000。
为了获得一个可执行文件或dll文件被加载到进程地址空间的位置,可以使用GetModuleHandle函数。它需要一个以/0结尾标示可执行文件或dll的名字字符串为参数。当传入NULL时,此时将会返回主调进程可执行文件的基地址,即使此时代码在一个dll文件中仍然是这样。如果此时代码在dll中执行,我们想何知道此时代码正在什么模块中运行,这可以通过GetModuleHandleEx得到。将GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS作为它的第一个参数,再将当前函数的地址作为它的第二个参数,函数执行完毕,后一个参数将保存出入的函数所在dll的基地址。
系统在创建进程时会传给他一个命令行,这个命令行总是非空,因为它至少存储有可执行文件的名称。C运行库的启动代码在执行一个GUI应用程序时,会调用windows函数GetCommandLine来获得进程的完整命令行,它忽略可执行文件名称,然后将剩余部分传给WinMain的pszCmdLine参数。
每个进程都有一个与它相关联的环境块。用以定义工作环境、保存有用信息,使系统获得相关设置。应用程序经常利用环境变量让用户精细其行为。用户创建一个环境变量并进行初始化,此后应用程序运行时会正在环境块中查找变量,如果找到变量会解析变量的值,并调整自己的行为。它所占用的内存是在进程地址空间内分配的。同样调用GetEnvironmentStrings函数可以获得完整的环境块。通常子进程会继承一组环境变量,这些环境变量和父进程的环境变量相同,父进程可以控制那些环境变量允许子进程继承。注意子进程继承的仅仅是父进程环境变量的副本,它们不共享同一个环境块。GetEnvironmentVariable函数可以用来判断一个环境变量是否存在。
在下图中我们可以看到形如%USERPROFILE%的字符串,它表示两个%之间的这部分内容是一个可替换的变量。该变量在环境变量中已经被定义。
可以使用SetEnvironmentVariable来添加、删除或修改一个变量。
Windows不建议使用入口函数的参数来访问命令行或是环境变量,而应该使用以上介绍的各种函数。应该将它们当做只读变量,不要对它们进行修改。
在多处理器的系统中,可以强迫线程在某个cpu上运行,这成为处理器关联性。子进程继承了其父进程的关联性。
如果不提供完整的路径名,windows函数会在当前驱动器的当前目录查找文件和目录。如:调用CreateFile打开一个文件时,如果仅指定文件名,系统将在当前驱动器和目录查找该文件。
系统在内部跟踪记录这一个进程当前驱动器和目录,这些信息是以进程为单位来维护的,如果该进程的一个线程更改了当前驱动器和目录,则只影响本进程的所有线程。
一个线程可以使用GetCurrentDirectory和SetCurrentDirectory来获得和设置当前驱动器和目录。子进程的当前目录默认为每个驱动器的根目录。如果父进程希望子进程继承它的当前目录,必须在生成子进程之前,添加环境变量。
使用GetVersionEx可以获得window系统的版本号。
相关推荐
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11