一个进程可以有四种方式终止:
这个方式是最正常也是最值得推荐的方式,主线程的入口点函数返回确保了:任何由这个线程创建的 C++ 对象会通过该对象的析构函数清理;操作系统会释放由线程栈使用的内存;系统会将线程的入口点函数的返回值设置为进程的退出码(这个退出码由进程内核对象维护);系统将减少内核对象的使用计数。
进程内的一个线程调用了 ExitProcess 函数后,进程会终止执行。
当前主线程的入口函数返回时,它返回给 C/C++ 运行时启动代码,这些代码会清理所有的进程使用的 C/C++ 运行时资源,最后 C/C++ 运行时启动代码会显式调用 ExitProcess 函数,并传递你的入口函数返回的代码来终止进程,所以如果主线程返回,不管是否有其他线程是否还在运行,进程都会终止。如果在你的主线程中调用了 ExitProcess 函数或 ExitThread 函数,进程或线程立即成为终止状态,C/C++ 运行时启动代码就没有机会执行清理操作。
Windows Platform SDK 文档指出,只要有线程还在执行,进程就不会终止,这是对的,可是在我们的应用程序中有了 C/C++ 运行时启动函数的参与,情况才变得不同。如果主线程在最后使用了 ExitThread 函数,那么运行时启动代码不会知道主线程已经终止,它也就没有机会调用 ExitProcess 线程,那么,一切都与 SDK 文档指出的那样,等所有线程都退出了,进程也就自然终止了,但是最好不要这样做,我们还需要 C/C++ 运行时启动函数为我们做一次清洁工。
任何进程都可通过 TerminateProcess 函数来终止一个进程,包括终止本身。这个方法可以立即终止一个进程的执行,这个进程没有方法避免自身被终止,它不会收到任何有关要终止的通知,所以没有机会清理它的工作,例如,进程可能不会将任何信息保存到磁盘上。进程终止后,系统会清理进程的所有资源,包括内核对象的使用计数减少、进程占有内存的释放等。
这个函数是异步执行的,也就是说在指定进程被终止之前它可能已经返回了。
如果进程中的所有线程终止了(线程调用 ExitThread 或通过 TerminateThread 被终止),那么进程也就终止了,进程的退出码是最后一个终止线程的退出码。
当一个进程终止时,会有下列一系列的动作:
1. 进程内的任何线程都会终止
2. 所有由进程分配的用户对象和 GUI 对象被释放,所有内核对象被关闭
3. 进程的退出代码从 STILL_ACTIVE 变成 ExitProcess 或 TerminateProcess 传递的代码
4. 进程内核对象变成“有信号”状态
5. 进程内核对象的使用计数减 1
内核对象的生存期与进程自身一样长,甚至还要长于它的进程,父进程在创建子进程后,如果没有关闭返回的子进程的句柄,就可能发生这种情况。父进程可以通过 GetExitCodeProcess 来获取子进程的退出码,这个函数会查询进程内核对象,对这个对象的成员进行分析,将结果返回这个函数的第二参数,如果要查询的进程没有退出,退出码会是 STILL_ACTIVE。