分析进程、线程的终止

来源:互联网 发布:软件质量管理指南 编辑:程序博客网 时间:2024/04/30 21:44

PROCESS_ALL_ACCESS包含PROCESS_TERMINATE

当需要终止进程时,需要是PROCESS_TERMINATE,因为他有终止的权限,PROCESS_ALL_ACCESS只有读写的权限。


如果是有窗口的,使用spy++找出它的classname,或者,caption,
假如,它的classname是MyProc;
HWND hWnd;
hWnd = FindWindow("MyProc",NULL);
SendMessage(hWnd,WM_CLOSE,0,0);
搞定了!
如果不是基于窗口的,像一些变态的人写的病毒啦。。等等。。
可以使用Process32First,Process32Next,(可能是这个函数名字)
等等,找到他的进程名字。

然后
TerminateProcess()搞定。


退出线程可以有四种方法:

1.线程函数的return返回(最好这样):
其中用线程函数的return返回, 而终止线程是最安全的, 在线程函数return返回后, 会清理函数内申请的类对象, 即调用这些对象的析构函数. 然后会自动调用 _endthreadex()函数来清理 _beginthreadex(...)函数申请的资源(主要是创建的tiddata对象).

2.调用 _endthreadex()函数 或 ExitThread()函数(最好不要):
如果使用这两种方法退出线程, 则不会执行线程函数的return语句, 所以就不会调用线程函数作用域内申请的类对象的析构函数, 会造成内存泄露.

剩下两种是在程序设计中一定要避免的.
3.用同一个进程中的另一个线程调用 TerminateThread()函数(必须避免);
4.终止该线程所在的进程(绝对避免);

但是要说_endthreadex完全没有用肯定是不对的,_endthreadex并不是一个过时的函数,正确的使用并不会带来问题。
比如在线程的主函数中,return是_endthreadex的一个良好替代,就像main函数里面return是exit()或ExitProccess()的良好替代一样,但是这不表示exit函数没用。比如线程调用了一个子函数,如果子函数决定退出线程,return是没用的,_endthreadex即可终结线程。

但是这个设计不好,因为可能造成LZ提出的资源泄漏。尤其考虑到后台线程终结后的资源泄漏比主线程的资源泄漏更要命(主线程退出后,进程就退出了,OS会清理一切资源,无所谓泄露不泄露,而子线程退出后主线程可能还会运行很久,并且可能有大量的同类型的子线程退出,会造成要命的泄露)
良好的设计还是返回线程的主函数,让threadproc来决定是不是要退出,从这个意义上说,_endthreadex没有必要。微软也指出,有些程序员就是要调用exit系列函数(ExitThread,ExitProccess等),没辙,只好提供了。


结束进程

进程可以以4种方式结束:

  • 主线程入口点函数(_tmain/_tWinMain)返回时结束,建议所有进程都以这种方式结束
  • 进程中的某线程调用了ExitThread函数,应当避免这种情况
  • 另一进程中的某线程调用了TerminateProcess函数,应当避免这种情况
  • 进程中的所有线程中止,这种情况比较罕见

下面我们来分别讨论这4种情形。

 

主线程入口点函数返回

在设计应用程序时,你应该保证其进程只有在主线程入口点函数返回时才终止,这也是确保能够正确释放主线程使用的所有资源的唯一途径。主线程入口点函数正常返回时,系统将会执行下面的动作:

  • 主线程创建的所有C++对象将被正确的销毁
  • 主线程堆栈占用的内存空间将被释放
  • 系统会把入口点函数的返回值作为进程的退出码写入进程内核对象中
  • 进程/主线程内核对象引用计数减1

调用ExitProcess函数

当进程中的任意线程调用ExitProcess函数时,进程将被终止。我们在前面的章节提过,C/C++运行时启动函数在主线程入口点函数 (WinMain、wWinMain、main或wmain)返回时,会清理进程使用的所有C运行时资源,然后显式调用ExitProcess,并将入口点函 数的返回值传给ExitProcess。这也解释了主线程入口点函数返回会导致进程中止的原因,注意此时进程中的所有线程也会随进程 一起终止。

根据Windows Platform SDK文档的说法,当进程中仍然有线程在执行时,进程不会终止。对于操作系统而言这是正确的,可是 C/C++运行时的实现却并非如此:当主线程入口点函数返回时,无法有进程中是否有其它正在执行的线程,进程都会被 ExitProcess函数终止。然而如果你在入口点函数中调用了ExitThread而不是将其返回或调用ExitProcess,主线程会停止执行, 但进程会一直存在直到其中最后一个线程中止。

调用ExitThread和ExitProcess会导致线程或进程在函数内立刻终止,对于操作系统来说这无关紧要,因为系统会保证释放线程/ 进程用到的系统资源,但是,C/C++程序应该避免调用这些函数,C/C++运行时可能无法及时执行清理工作,比如下面的代码。 view plai

#include <windows.h>  #include <stdio.h>    class CSomeObj {  public:     CSomeObj()  { printf("Constructor/r/n"); }     ~CSomeObj() { printf("Destructor/r/n"); }  };      CSomeObj g_GlobalObj;    void main () {     CSomeObj LocalObj;     ExitProcess(0);    // This shouldn't be here       // At the end of this function, the compiler automatically added     // the code necessary to call LocalObj's destructor.     // ExitProcess prevents it from executing.  } 

 c 

执行上面的代码,输出如下:


[c-sharp] view plain copy
  1. Constructor  
  2. Constructor  
  代码创建了两个对象,一个全局对象一个局部对象。然而,ExitProcess的调用导致对象没有被正确的析构,在移除ExitProcess 之后,程序输出正常结果:
[c-sharp] view plain copy
  1. Constructor  
  2. Constructor  
  3. Destructor  
  4. Destructor  
  

调用TerminateProcess函数

TerminateProcess也会导致进程中止:

[c-sharp] view plain copy
  1. BOOL TerminateProcess(HANDLE hProcess, UINT fuExitCode);  
  与ExitProcess最大的不同在于,TerminateProcess除了可以中止当前进程,还可以中止其它进程。参数hProcess指定了要终止的 进程句柄,其退出码由fuExitCode指定。当你无法使用别的方法结束某进程时,你才应该调用TerminateProcess。被结束的进程 不会得到任何关于它将被终止的通知,它也没机会做任何清理工作。然而进程用到的系统资源将被系统释放,记住,当进程终止 时,它用到的一切资源都会回收。TerminateProcess是异步方法,这意味着它返回时目标进程可能尚未被终止,因此你可能需要 调用WaitForSingleObject等待其结束。

所有线程中止

当进程内的所有线程终止时,操作系统将销毁进程地址空间,进程的退出码将被设置为最后一个线程的退出码。

进程终止时发生了什么

当进程终止时,下面的动作会被触发:

  • 进程内的所有线程将被终止
  • 进程创建的所有的用户对象和GUI对象将被释放,其引用的所有内核对象引用计数减1
  • 进程的退出码从STILL_ACTIVE变为ExitProcess或TerminateProcess函数的相应参数
  • 进程的内核对象状态变成signaled(关于内核对象状态,可参阅第9章)
  • 进程的内核对象引用计数减1

 

注意进程内核对象的生命周期和进程的生命周期并不完全相同,进程终止只是将其内核对象的引用计数减1,如果此时另外的进程 仍持有该对象的句柄,那么进程内核对象并不会销毁。这通常发生在父进程忘记关闭子进程的句柄时,这是一个特性,而不是BUG 。进程内核对象维护着进程的统计信息,这些信息在进程终止之后依然是可用的。比如,你可以获得进程占用的CPU时间总数,或 者用GetExitCodeProcess获得其退出码:

[c-sharp] view plain copy
  1. BOOL GetExitCodeProcess(HANDLE hProcess, PDWORD pdwExitCode);  
  当目标进程尚未结束时,GetExitCodeProcess的pdwExitCode会返回STILL_ACTIVE,否则返回其退出码。你可能会想到可以在一个 循环中使用GetExitCodeProcess以判断某进程是否结束,这的确可以,不过显然效率比较低, 在下一节我会提到一种更恰当的判 断进程是否结束的方法。

再说一遍,当你对进程的统计信息不再感兴趣时,要记得调用CloseHandle关闭内核对象句柄,否则进程内核对象会一直存在。


1 0
原创粉丝点击