Win32 exe 文件的啓動過程(zz)

 學習windows 編程從mfc角度來說可分爲兩部分那就是WinMain函數以前的,和WinMain函
數以後的。前者涉及很多windows操作系統內部的知識,後者需要看mfc源碼。雖然大多數程序不需要了解太多關於os加載應用程序這方面的知識,但能較深入瞭解windows os的運行情況對程序員是很有幫助的。
     關於os如何加載程序,它包括進程創建,主線程 創建,PE文件加載,程序c運行時啓動函數以及四種
main函數的調用等。從mfc編程角度來說,這些都是不得見的,不過了解這些對程序員編制好的windows程序是有好處的。到底在桌面雙擊一個exe程序,os調用的第一個函數是什麼?

     要了解一個.exe程序的啓動過程就不得不瞭解一下有關操作系統方面的知識,such as“進程,線程,虛擬內存"的基本的知識。

     未真正開始之前,先統一一下本文出現的一些名詞的含義:

     App.exe----------假定爲應用mfc的AppWizard做出的一個SDI程序,App是它的名字。你
可以把它看爲一個標準的"hello mfc!"程序。
      PE------------不要以爲它是“體育課”的縮寫呦。它可是微軟的標準win32可執行文件
.exe和動態鏈接庫.dll的文件格式,它的english name是Portable Executable File Forma
t。
   下面可要正式開始了。
    一個microsoft的.exe程序的啓動方法有很多,這裏我們以雙擊App.exe圖標啓動爲例(其他方法,我想也是一樣的)。在補充一下,我所用的os是Windows2000Server,所以這裏也
主要討論win2000下的應用程序,過要涉及較多關於NT內核,畢竟微軟主推win2000/winxp和
Unicode。
    一個microsoft的.exe程序的啓動過程如下:
(1)當我們雙擊App.exe圖標啓動程序時,系統首先做什麼呢,讓我們先聽一聽侯捷是如何說的吧“執行起來的App進程其實是shell調用CreateProcess激活的”----"深入淺出MFC second edition" page39載。很多書上都是如是說的,shell又名“命令解釋器”,是win32操作系統基於瀏覽器的一個32位用戶接口,它是一個多線程的好例子,屏幕上每一個文件夾瀏覽窗口都是它的一個線程。它是操作系統引導時加載的系統進程,它具體表現爲windows explorer.exe。explorer.exe是所有用戶應用程序的創造者。你完全可以將shell看成是所有應用程序進程的父進程,就像桌面(desktop)可看成所有窗口的父窗口一樣。shell的用途很多,如啓動應用程序,管理文件系統,將應用程序與相應文件相關聯等等。我們常見的桌面上的帶有小箭頭的快捷方式(shortcut)就是一個shell鏈接,shell負責管理一個叫"名字空間"的類似文件系統似的“超文件系統”,它允許應用程序在任何地方在不知訪問對象名字和位置的前提下訪問到這個對象,此類對象有:文件,目錄,驅動器,打印機以及網絡資源。而名字空間就是shell把這些對象有層次組織起來的一個結構。名字空間爲用戶和應用程序提供了一種可靠和高效的方法來訪問和管理對象。好了不論它是什麼,凡正它調用了CreateProcess,一切就從這裏開始了。


(2)CreateProcess這個函數可作了不少工作。App進程由此誕生。當CreateProcess這個函數被調用,系統就會創建一個“進程內核對象”。進程內核對象可以看作一個操作系統用來管理進程的內核對象,它也是系統用來存放關於進程統計信息的地方(一個小的數據結構),其實它的真正創建者是一個叫NTCreateProcess的windows2000系統服務函數(也叫執行體服務函數),他創建了進程內核對象供用戶擴展。進程內核對象的初始使用計數爲1。然後系統爲該進程創建4GB(=2^32)的虛擬地址空間(所謂虛擬就不是真的創建4GB的物理內存空間,這些空間不是真在物理內存上).用於加載App.exe可執行文件和任何必要的dll文件的數據和代碼。


(3)下面概述一下系統的加載器(可稱爲loader)是如何加載這些東東的。首先了解一下系統爲該進程創建4GB的虛擬地址空間是如何分配的,對於win2000/winxp來說,默認情況下每個用戶進程可以佔有2GB的私有地址空間;操作系統佔有剩餘的2GB空間。在32位x86系統上,


從0x00000000到0x7fffffff的空間中存放着 應用程序代碼,全局變量,每個線程堆棧,dll
代碼。
從0x80000000到0xc0000000的空間中存放着 內核和執行體,HAL(硬件抽象層),引導驅動程序

從0xc0000000到0xc0800000的空間中存放着 進程頁表和超空間。
從0xc0800000到0xffffffff的空間中存放着 系統高速緩存,分頁緩衝池,非分頁緩衝池。


首先,CreateProcess打開應用程序文件(.exe),它先掃描該文件的文件頭,該文件頭裏含有文件能運行在那個環境之下,如果是win32環境,系統就直接加載文件的代碼和數據並輸入(import)該文件執行所需的dll函數。如果不是win32環境比如時os/2的.exe則先加載相應的環境子系統,載由該環境加載該文件的代碼和數據以及該文件執行所需的dll函數。至於系統是如何知道文件的代碼和數據以及該文件執行所需的dll函數所在的位置就需要你瞭解一下PE文件格式了,其實也很簡單,PE文件擁有很多sections,數據和代碼都放在不同的section裏面,文件執行所需的dll也放在單獨的section(.idata)裏,這裏就不詳述了。而且在加載過程中涉
及到有關虛擬內存,內存映射文件等很多較深的知識,我會在以後的系列文章中詳細專題論述的。


(4)進程加載代碼和數據完畢後,就開始創建線程來執行進程空間內的代碼。進程是靜態的,它只是線程的容器。一個進程至少因該有一個線程(main thread),其它線程都是主線程通過調用CreateThread函數創建的。線程也是核心對象,他的實際創建者是一個叫NtCreateThread的windows2000系統服務函數。一個線程其實只是一個線程核心對象和兩個堆棧(一個核心堆棧,用於線程運行在覈心態;一個用戶堆棧,用於線程運行在用戶態),線程與進程類似,也擁有線程核心對象計數和線程句柄,這裏不詳述。線程用於描述進程中的運行路徑。每當進程被初始化時,系統就要創建一個主線程。該線程與c/c++運行時庫的啓動代碼一道開始運行,啓動代碼則調用進入點函數(就是我們的main函數,它也是主線程的進入點函數),並且繼續運行直到進入點函數返回並且c/c++運行時庫的啓動代碼調用ExitProcess爲止。每個線程都有自己的入口點函數,主線程入口點函數名字必須是main,wmain,WinMain或wWinMain.而其他的線程入口點函數名字可使用任何名字。每個線程函數必須有一個返回值,它將作爲線程的退出代碼。對於主線程來說,這個返回值將傳給c/c++運行時庫的啓動函數。

(5)c/c++運行時庫的啓動函數它其實是一個程序的真正調用的第一個函數,它是在程序鏈接時由鏈接程序選擇相應的啓動函數並加到程序的開始處。c/c++運行時庫有四個版本的啓動函數,他們分別對應不同類型的應用程序。比如,需要ANSI字符和字符串的GUI應用程序的啓動函數是WinMainCRTStartup,其對應的進入點函數是WinMain,需要Unicode字符和字符串的GUI應用程序的啓動函數是wWinMainCRTStartup,其對應
的進入點函數是wWinMain,而需要ANSI字符和字符串的CUI應用程序(如控制檯console程序)的應用程序的啓動函數是mainCRTStartup,對應的入口點函數爲main;需要Unicode字符和字符串的CUI應用程序(如控制檯console程序)的應用程序的啓動函數爲wmainCRTStartup,對應的入口點函數爲wmain;c/c++運行時庫的啓動函數的功能如下:


以wWinMainCRTStartup(大多數運行在windows2000下的應用程序的啓動函數都是它)爲例。它負責:
*檢索指向新進程的完整命令行指針;
*檢索指向新進程的環境變量的指針;
*對c/c++運行時的全局變量進行初始化;
*對c運行期的內存單元分配函數(比如malloc,calloc)和其他低層I/O例程使用的內存棧進行初始化。
*爲C++的全局和靜態類調用構造函數。
當這些初始化工作完成後,該啓動函數就調用wWinMain函數進入應用程序的執行。
當wWinMain函數執行完畢返回時,wWinMainCRTStartup啓動函數就調用c運行期的exit()函數,將返回值(nMainRetVal)傳遞給它。之後exit()便開始收尾工作:
*調用由_onexit()函數調用和註冊的任何函數。
*爲C++的全局和靜態類調用析構函數;
*調用操作系統的ExitProcess函數,將nMainRetVal傳遞給它,這使得操作系統能夠撤銷進
程並設置它的exit 代碼。


(6)至此啓動函數的任務完成,至於中間wWinMain函數的運行過程看看mfc源碼即可。不過我還要提一下,wWinMain函數其實只是調用了mfc的AfxWinMain()函數,而一切的真正代碼的運
行也是從AfxWinMain()開始的。
   以上只是粗略將一下一個microsoft的.exe程序的啓動過程,其中有很多深奧的知識我只是提了一下,有些知識在以後的文章中還會陸續提到的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章