exit(0),ExitProcess和ThrminateProcess的區別的聯繫

首先來談談一個進程的執行流程。每個應用程序都有個主函數,在WINDOWS下,只支持兩種類型的應用程序——CUI(控制檯應用程序)和GUI(圖形界面應用程序),相應的,其主函數類型不同。來看下這幾個入口函數
[cpp] view plaincopyprint?
1. int WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE,PSTR pszCmdLine, int nCmdShow); 
2.  
3. int WINAPT wWinMain(HINSTANCE hinstExe,HINSTANCE,PWSTR pszCmdLine,int nCmdShow); 
4.  
5. int __cdecl main(int argc,char *argv[],char *envp[]); 
6.  
7. int _cdecl wmain(int argc, wchar_t *argv[],wchar_t *envp[]); 
 
前兩個爲GUI的入口函數,後兩個爲CUI的入口函數;事實上,在一個進程開始運行時,WINDOWS OS並不直接從主函數開始執行,而是從另外
一個比較大的運行期啓動函數開始執行,不同的入口函數對應的啓動函數不同:
應用程序類型                                                       進入點                嵌入可執行文件的啓動函數
需要ANSI字符和字符串的GUI應用程序               WinMain             WinMainCRTStartup
需要Unicode字符和字符串的GUI應用程序          WinMain             WinMainCRTStartup
需要ANSI字符和字符串的CUI應用程序               main                   mainCRTStartup
需要Unicode字符和字符串的CUI應用程序          wmain                 wmainCRTStartup

啓動函數負責對應用程序運行前期的初始化,如全局變量的內存分配等。如果編寫了一個wWinMain()函數,以下就是它的調用過程:
[cpp] view plaincopyprint?
1. GetStartupInfo(&StartupInfo); 
2. int nMainRetVal = wWinMain(GetMjduleHandle(NULL), 
3.    NULL, pszCommandLineUnicode, 
4.    (StartupInfo.dwFlags & STARTF_USESHOWWINDOW) ?  
5.    StartupInfo.wShowWindow:SW_SHOWDEFAULT); 
6.  
7. ...... 
8.  
9. exit(0) 
 
當入口函數(主函數)返回時,運行期啓動函數就執行EXIT函數,此函數主要完成全局對象和變量的內存釋放任務,之後:再調用ExitProcess
函數進行撤銷進程。即:exit()函數內部調用了ExitProcess函數。通常來說,這是最完美的進程執行過程。由此可以看出eixt()函數原型:
進行全局變量和對象的析構,然後調用ExitProcess函數。
注意:它只析構全局對象和變量,而不析構局部變量,後面我會列出具體事例程序來說明。
ExitProcess()函數實際上只是用來進行結束進程,如果其後面還有我們預期要執行的代碼,實際上全未執行,這個性質暴露出ExitProcess函數
的缺陷:可能導致已經創建的對象沒有析構而退出,從而導致內存泄露。
TerminateProcess()函數的實際作用跟ExitProcess函數差不多,只不過,此函數可用來終止當前進程之外的另外一個其它進程,同樣的,它
也會導致和ExitProcess一樣的結果:內存泄露。
如果我們在編寫應用程序時,打算終止當前進程,我們該調用哪個函數?答案是:三者其實都一樣! 因爲三者都可能導致內存泄露,但我們擔心
的過多了,因爲進程在結束時,即使有ExitProcess,TerminateProcess,以及exit函數調用而導致的內存泄露,OS也會進行清理工作,能保證
我們泄露的內存最終被還回到OS中去,而不必擔心當前進程已經退出而導致內存泄露,致使其它進程無法使用該內存塊。一個進程無論在什麼情
況下終止,都會進行如下工作:
1) 進程指定的所有用戶對象和G D I對象均被釋放,所有內核對象均被關閉(如果沒有其他 進程打開它們的句柄,那麼這些內核對象將被撤消。
    但是,如果其他進程打開了它們的句柄, 內核對象將不會撤消)。 
2) 進程的退出代碼將從S T I L L _ A C T I V E改爲傳遞給E x i t P r o c e s s或Te r m i n a t e P r o c e s s的代碼。 
3) 進程內核對象的狀態變成收到通知的狀態(關於傳送通知的詳細說明,參見第9章)。系 統中的其他線程可以掛起,直到進程終止運行。 
4) 進程內核對象的使用計數遞減1。
下面來看下如下很簡單的示例程序:
[cpp] view plaincopyprint?
1. // exitprocess_text.cpp : 定義控制檯應用程序的入口點。 
2. // 
3.  
4. #include "stdafx.h" 
5. #include "windows.h" 
6. #include "iostream" 
7.  
8. class Example 
9. { 
10. public: 
11.  
12.     Example() 
13.     { 
14.         std::cout<<"struction"<<std::endl; 
15.  
16.     } 
17.  
18.     ~Example() 
19.     { 
20.         std::cout<<"construction"<<std::endl; 
21.     } 
22. }; 
23.  
24. Example ex1; 
25.  
26. int _tmain(int argc, _TCHAR* argv[]) 
27. { 
28.      
29.     Example ex2; 
30.      
31.     return 0; 
32. } 
 
程序這樣執行時最完美的,其輸出結果如下:
structionstructionconstructionconstruction
局部對象ex1和全局對象ex2都被正常析構,而如果將主函數該爲如下:
[cpp] view plaincopyprint?
1. int _tmain(int argc, _TCHAR* argv[]) 
2. { 
3.      
4.     Example ex2; 
5.  
6.     ::ExitProcess(0); 
7.      
8.     return 0; 
9. } 
 
程序此時執行結果爲:
structionstruction
局部對象和全局對象都沒被析構,因爲調用了ExitProcess進程直接結束,而沒有調用啓動函數中的exit函數,所以全局對象也沒被析構。
而如果將主函數再改爲如下:
[cpp] view plaincopyprint?
1. int _tmain(int argc, _TCHAR* argv[]) 
2. { 
3.      
4.     Example ex2; 
5.  
6.     exit(0); 
7.      
8.     return 0; 
9. } 
   程序此時執行結果爲:
structionstructionconstruction

可以看到只析構了一個對象,而另外一個未被析構,其實沒被析構的對象是局部對象,前面提到exit函數主要任務就是負責析構全局對象和變
量,而不負責局部對象的析構。爲了說明析構的是全局變量,將主函數再做如下處理:
[cpp] view plaincopyprint?
1. int _tmain(int argc, _TCHAR* argv[]) 
2. { 
3.      
4.     exit(0); 
5.      
6.     return 0; 
7. } 
  
代碼的執行結果是:
structionconstruction
由此證明析構的是全局變量;如果你認爲還不給力的話,試着把全局對象刪掉,局部對象留下,其執行結果是:
struction
局部變量會被證明沒被析構,這絕度沒有任何含糊。



發佈了33 篇原創文章 · 獲贊 10 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章