《Windows核心编程系列》谈谈进程的建立和终止

发表于:2012-10-09 10:04

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:ithzhang    来源:51Testing软件测试网采编

  上一章介绍了内核对象,这一节开始就要不断接触各种内核对象了。首先要给大家介绍的是进程内核对象。进程大家都不陌生,它是资源和分配的基本单位,而进程内核对象就是与进程相关联的一个数据结构。操作系统内核通过它管理进程,也就是操作系统原理上介绍的进程控制块(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系统的版本号。

51/512345>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号