一個microsoft的.exe程式檔案的啟動過程 (轉)

worldblog發表於2007-12-13
一個microsoft的.exe程式檔案的啟動過程 (轉)[@more@]一個的.exe的啟動過程
  學習 從mfc角度來說可分為兩部分那就是WinMain以前的,和WinMain函
數以後的。前者涉及很多windows操作內部的知識,後者麼看mfc原始碼就可以了。雖然大
多數程式不需要你瞭解太多關於os載入應用程式這方面的知識,但我認為能較深入瞭解wind
ows os的執行情況對程式設計師是很有幫助的。
  最近我正在研究一個microsoft的.exe程式的啟動過程,這也是以上所說的關於os如何加
載程式的知識,它包括程式建立,主執行緒 建立,PE載入,程式c執行時啟動函式以及四種
main函式的等許多令我不得不靜下心來好好思索的東東。從mfc程式設計角度來說,這些都是
不得見的,不過了解這些對程式設計師編制好的windows程式是有好處的。在平時的學習中中我有
很多疑點,到底在桌面雙擊一個exe程式,os呼叫的第一個函式是什麼?甚至到現在我研究很
長時間後,還是得不到令我滿意的答案。不過在學習的過程中我還是又說收穫的,下面和大
家一起分享一下,我也把這段時間所學作一下總結。
  要了解一個.exe程式的啟動過程就不得不瞭解一下有關方面的知識,such as“
程式,執行緒,虛擬"的基本的知識。當然這裡我就不詳細介紹了,有興趣的同學可以自己
去查一查這方面的資料。在未真正開始之前,先統一一下本文出現的一些名詞的含義:
  App.exe----------假定為應用mfc的AppWizard做出的一個SDI程式,App是它的名字。你
可以把它看為一個標準的"hello mfc!"程式。
  PE------------不要以為它是“體育課”的縮寫呦。它可是的標準可檔案
.exe和動態連結庫.dll的檔案格式,它的english name是Portable Executable File Forma
t。
  下面可要正式開始了。
  一個microsoft的.exe程式的啟動方法有很多,這裡我們以雙擊App.exe圖示啟動為例(
其他方法,我想也是一樣的)。在補充一下,我所用的os是Server,所以這裡也
主要討論下的應用程式,過要涉及較多關於NT核心,畢竟微軟主推win2000/和
Unicode麼。
  一個microsoft的.exe程式的啟動過程如下:
  (1)當我們雙擊App.exe圖示啟動程式時,系統首先做什麼呢,讓我們先聽一聽侯捷是如何
說的吧“執行起來的App程式其實是呼叫CreateProcess啟用的”----"深入淺出MFC se
cond edition" page39載。很多書上都是如是說的,shell又名“命令直譯器”,是win32操
作系統基於的一個32位介面,它是一個多執行緒的好例子,螢幕上每一個資料夾瀏
覽視窗都是它的一個執行緒。它是作業系統引導時載入的系統程式,它具體表現為windows ex
plorer.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環境,系統就直接載入檔案的程式碼和資料並輸入(im
port)該檔案執行所需的dll函式。如果不是win32環境比如時os/2的.exe則先載入相應的環境
子系統,載由該環境載入該檔案的程式碼和資料以及該檔案執行所需的dll函式。至於系統是如
何知道檔案的程式碼和資料以及該檔案執行所需的dll函式所在的位置就需要你瞭解一下PE檔案
格式了,其實也很簡單,PE檔案擁有很多sections,資料和程式碼都放在不同的section裡面,
檔案執行所需的dll也放在單獨的section(.idata)裡,這裡就不詳述了。而且在載入過程中涉
及到有關虛擬記憶體,記憶體對映檔案等很多較深的知識,我會在以後的系列文章中詳細專題論
述的。
  (4)程式載入程式碼和資料完畢後,就開始建立執行緒來執行程式空間內的程式碼。程式是靜態的
,它只是執行緒的容器。一個程式至少因該有一個執行緒(main thread),其它執行緒都是主執行緒通
過呼叫CreateThread函式建立的。執行緒也是核心物件,他的實際建立者是一個叫NtCreateTh
read的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,對應的入口點函式為wma
in;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程式的啟動過程,其中有很多深奧的知識我只是提了一下,有些知識在以後的文章中還會陸續提到的。
  bigwhite
  2002.5.18

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992452/,如需轉載,請註明出處,否則將追究法律責任。

相關文章