進程

     進程這個詞我們在操作系統中經常要到 ,但是什麼是進程呢?現在就來說下的它的定義。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對象將被清除

 

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