进程

     进程这个词我们在操作系统中经常要到 ,但是什么是进程呢?现在就来说下的它的定义。WINDOWS核心编程是这样定义它的:进程的就是一个正在运行的程序的实例。它有两部分组成:

  • 管理进程的内核对象(进程内核对象),这个内核对象就是是一个简单的数据结构,保存系统中进程的一个统计信息。
  • 运行程序的地址空间。包括EXE模块,所有的Dll模块,线程的堆栈,堆等等)

plus:

  • 一个进程至少有一个线程来执行地址空间中的代码
  • 在创建进程的时候,系统会自动给它创建主线程,主线程可以创建其他线程,而其他线程又可以再创建线程

用过c/c++写过程序的人都知道。写一个可以执行的程序肯定要有这么几个函数main(),wmain(),WinMain(),wWinMain()。这些都叫做进入点函数。WinMain()和wWinMain()是Win32程序的进入点函数,main()和wmain()是控制台程序的入口点函数。既然叫入口点函数就是程序开始执行的地方。但其实进程进入运行的启始点并不是他们,而是c runtime library的启动函数,他们是mainCRTStartup(),wmainCRTStartup(),WinMainCRTStartup(),wWinMainCRTStartup()。对应上面说的4种进入点函数,分别是先调用这4个启动函数。那么为什么要先调用这4个启动函数呢?现在就来说说这4个启动函数的功能

  • 检索指向新进程的完整命令行指针
  • 检索指向新进程的环境变量
  • 对c/c++ runtime library的全局变量进行初始化。如果你的程序中包含StdLib.h,你就可以访问这些变量
  • 初始化由c runtime的内存分配函数(malloc and calloc)使用的堆(heap),以及初始化其他底层的输入输出方法
  • 为所有全局和静态c++类调用构造函数

好了,当所有的这些工作做完了之后,就调用你的进入点:

GetStartupInfo(&StartupInfo);

int nMainRetValue = WinMain(GetModuleHandle(NULL),NULL,pszCommandLineAnsi,(StartupInfo.dwFlags&STARTF_USESHOWWINDOW)?StartupInfo.wShowWindow:SW_SHOWDEFAULT);

当进入点函数返回时,启动函数便调用c runtime的exit(),将返回的nMainRetValue传递给它。Exit()函数包括下面的操作:

  • 调用所有通过_onexit()函数注册的方法
  • 为所有全局及静态c++类对象调用析构函数
  • 调用操作系统的ExitProcess(),传递nMainRetValue给他,这使得操作系统结束进程,并设置Exit Code

下面来讨论下进程的实例句柄

        每个被加载到进程地址空间中的EXE文件和Dll模块均有一个唯一的句柄。可执行文件的句柄作为WinMain()的第一个参数来传递hInstance。这个值其实是可执行文件在地址空间中的基址。

        HANDLE GetModuleHandle(PCTSTR psz)可以得到一个EXE和Dll的句柄,当传递以0结尾的字符串时就会在进程的地址空间查找你想要得到的EXE或者Dll;传递NULL就返回EXE文件的HANDLE

前面说了启动函数的几个功能,其中有检索指向新进程的命令行和检索指向新进程的环境变量。那么现在就来仔细说下进程是如何加载命令行和环境变量的。先来说下命令行: 

             当一个进程运行的时候,会传递一个命令行。命令行一般不会是空的,至少有创建新进程的可执行文件的名字,但是CreateProcess可以接受一个只有作为字符串结尾标志的0来作为参数,因为c runtime启动函数运行的时候,它检索进程的命令行,跳过可执行文件名,并将指向命令行其余部分的指针传递给pszCommandLine。可以通过GetCommandLine来得到完整的命令行参数的指针。

再说下环境变量,环境变量其实是地址空间中包含的一个内存块,以这种形式保存:

VarName1=VarValue1/0/n
VarName2=VarValue2/0/n
VarName3=VarValue3/0/n



VarNameX=VarValueX/0/n
/0

注意等号前后的空格是有意义的。可以通过

DWORD GetEnviromentVariable()来得到环境变量。

     下面就来说一下CreateProcess函数,当一个线程调用CreateProcess()函数,系统会首先创建一个进程内核对象,然后就分配地址空间,加载模块,最后,为主线程创建一个线程内核对象,主线程最终调用WinMain()。。至于CreateProcess()的具体用法,参考MSDN。这里就不多说了。

说了创建进程就不能不说终止进程,其方法有四:

  • 主线程进入点函数返回(最好)
  • 某个线程调用ExitProcess(避免使用)
  • 另一个进程的线程调用TerminateProcess() (避免使用)
  • 进程中所有线程自动终止运行(几乎没发生过)

下面就来说明下各个方法的特点。

  • 主线程进入点函数返回是最好的方法,应用程序应该这么去设计。这是将所有线程的资源全部清楚的唯一的办法。让主线程进入点函数返回,能确保以下操作:
    • 该线程创建的所有c++对象将调用他们的析构函数
    • 操作系统能正确的释放该线程使用的堆栈
    • 系统会设置进程的exit code(由进程内核对象维护)为进入点函数的返回值
    • 系统递减进程内核对象的计数器
  • Void ExitProcess(UINT fuExitCode).这种方法将终止进程运行,并把进程的退出代码设置为fuExitCode当主线程的进入点函数返回时,它将返回给c/c++ runtime library的启动函数,这样,c/c++ runtime library的启动函数就能清除该进程使用的所有c/c++ runtime资源。当清除这个后,启动代码就显示调用ExitProcess,并将进入点函数传递给它。也就是说在程序中调用ExitProcess或者ExitThread来结束进程的话,就操作系统而言,这样是很好的,进程和线程的所有资源将被清除,但是如果应用程序是用c/c++写的,也就是说调用了c/c++ runtime library,那么c/c++ runtime也许就得不到机会进行清除了,因为线程已强制终止了。
  • BOOL TerminateProcess(HANDLE hProcess,UINT fuExitCode).这种方法能使任何线程终止其他进程。这种方法只有当没其他办法终止进程运行时才被使用。因为调用TerminateProcess(),被终止的进程将得不到任何进程要结束的消息,所以应用程序无法清除,会发生应用程序不能把内存的信息迅速送往磁盘之类的操作。虽然应用程序自己得不到清楚的机会,但是操作系统会完成清除操作,也就意味着,进程所有的资源将被释放,打开的文件将被关闭,内核对象的计数递减1,所有用户对象和GDI对象将被清除

 

 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值