一、 軟件執行流程
1、 軟件編譯流程
? 預編譯完成宏展開工作。
? 為每一個 .cxx 源文件編譯一個目標文件( .obj,.o ),目標文件中至少包含二進制的代碼段和數據段。在 cxx 源文件中可能會引用在其他 cxx/hxx 中定義的符號,也可能是自己定義的一些符號,這些作用域超過一個 cxx 文件的符號稱為“ public 符號”(例如非靜態函數)。因此每一個目標文件中也包含一個符號表,用于記錄自己引用的符號及自己提供的 public 符號。
? 編譯器合成這些目標文件成一個庫文件( .lib ),同時解析可以找到的符號引用。此時這個庫文件包含了二進制的代碼段和數據段,同樣也會包含一個符號表,因為有一些符號需要引用其他靜態 / 動態鏈接庫的導出符號。
? 鏈接器負責把目標的庫文件和所有需要引用的靜態 / 動態鏈接庫進行鏈接,即需要首先把靜態庫合成到可執行文件中。轉換相應的符號引用為地址,然后確保所引用的其他動態鏈接庫的符號存在。最后生成可執行文件,可執行文件的符號表只需要記錄導入符號表。
2、 軟件運行流程
? 當雙擊 App.exe 圖標啟動程序時,執行起來的 App 進程是 shell 調用 CreateProcess 激活的。 桌面上的帶有小箭頭的快捷方式 (shortcut) 就是一個 shell 鏈接, shell 負責管理一個叫 " 名字空間 " 的類似文件系統似的“超文件系統” , 它允許應用程序在任何地方在不知訪問對象名字和位置的前提下訪問到這個對象,此類對象有:文件,目錄,驅動器,打印機以及網絡資源。而名字空間就是 shell 把這些對象有層次組織起來的一個結構。名字空間為用戶和應用程序提供了一種可靠和高效的方法來訪問和管理對象。
? 當 CreateProcess 函數被調用,系統創建一個“進程內核對象”。進程內核對象可以看作一個操作系統用來管理進程的內核對象,它是系統用來存放關于進程統計信息的地方(一個小的數據結構),真正創建者是 NtCreateProcess 的系統服務函數 ( 也叫執行體服務函數 ) ,他創建了進程內核對象供用戶擴展。進程內核對象的初始使用計數為 1 。然后系統為該進程創建 4GB ( =2^32) 的虛擬地址空間 ( 所謂虛擬就不是真的創建 4GB 的物理內存空間,這些空間不是真在物理內存上 ). 用于加載 App.exe 可執行文件和任何必要的 dll 文件的數據和代碼。 系統為該進程創建 4GB 的虛擬地址空間,對于 windows 來說,默認情況下每個用戶進程可以占有 2GB 的私有地址空間;操作系統占有剩余的 2GB 空間。
在 32 位 x86 系統上 :
從
0x00000000
到
0x7fffffff
的空間中存放著 應用程序代碼,全局變量,每個線程堆棧,
dll
代碼。
從
0x80000000
到
0xc0000000
的空間中存放著內核和執行體,
HAL(
硬件抽象層
),
引導驅動程序。
從
0xc0000000
到
0xc0800000
的空間中存放著進程頁表和超空間。
從
0xc0800000
到
0xffffffff
的空間中存放著系統高速緩存,分頁緩沖池,非分頁緩沖池。
? CreateProcess 打開應用程序文件 (.exe), 它先掃描該文件的文件頭,該文件頭里含有文件能運行在那個環境之下,如果是 win32 環境,系統就直接加載文件的代碼和數據并輸入 (import) 該文件執行所需的 dll 函數。如果不是 win32 環境比如是 os/2 的 .exe 則先加載相應的環境子系統,再由該環境加載該文件的代碼和數據以及該文件執行所需的 dll 函數。
加載器負責把可執行文件的數據段和代碼段映射到進程的虛擬內存空間中,讀入可執行程序的導入符號表,然后根據這些符號表可以查找出該可執行程序所有依賴的動態鏈接庫。
? 進程加載代碼和數據完畢后,就開始創建線程來執行進程空間內的代碼。進程是靜態的,它只是線程的容器。一個進程至少因該有一個線程 (main thread), 其它線程都是主線程通過調用 CreateThread 函數創建的。線程也是核心對象,他的實際創建者是 NtCreateThread 系統服務函數。一個線程只是一個線程核心對象和兩個堆棧 ( 一個核心堆棧,用于線程運行在核心態;一個用戶堆棧,用于線程運行在用戶態 ) ,線程與進程類似,也擁有線程核心對象計數和線程句柄。線程用于描述進程中的運行路徑。
?
每當進程被初始化時,系統就要創建一個主線程。該線程與
c/c++
運行時庫的啟動代碼一道開始運行,啟動代碼則調用進入點函數
(
就是
main
函數,它也是主線程的進入點函數
)
,并且繼續運行直到進入點函數返回并且
c/c++
運行時庫的啟動代碼調用
ExitProcess
為止。每個線程都有自己的入口點函數,主線程入口點函數名字必須是
main,wmain,WinMain
或
wWinMain.
而其他的線程入口點函數名字可使用任何名字。每個線程函數必須有一個返回值,它將作為線程的退出代碼。對于主線程來說,這個返回值將傳給
c/c++
運行時庫的啟動函數。
c/c++
運行時庫的啟動函數是一個程序調用的第一個函數,它是在程序鏈接時由鏈接程序選擇相應的啟動函數并加到程序的開始處。
c/c++
運行時庫有四個版本的啟動函數,他們分別對應不同類型的應用程序。比如,需要
ANSI
字符和字符串的
GUI
應用程序的啟動函數是
WinMainCRTStartup,
其對應的進入點函數是
WinMain,
需要
Unicode
字符和字符串的
GUI
應用程序的啟動函數是
wWinMainCRTStartup,
其對應的進入點函數是
wWinMain,
而需要
ANSI
字符和字符串的
CUI
應用程序
(
如控制臺
console
程序
)
的應用程序的啟動函數是
mainCRTStartup,
對應的入口點函數為
main;
需要
Unicode
字符和字符串的
CUI
應用程序
(
如控制臺
console
程序
)
的應用程序的啟動函數為
wmainCRTStartup,
對應的入口點函數為
wmain
。
c/c++
運行時庫的啟動函數
(
以
WinMainCRTStartup
為例
)
的功能如下
:
檢索指向新進程的完整命令行指針;檢索指向新進程的環境變量的指針;對
c/c++
運行時的全局變量進行初始化;對
c
運行期的內存單元分配函數
(
比如
malloc
,
calloc)
和其他低層
I/O
例程使用的內存棧進行初始化。為
C++
的全局和靜態類調用構造函數。
? 啟動函數條用主函數,進入應用程序的執行。
? 當主函數執行完畢返回時,啟動函數就調用 c 運行期的 exit() 函數,將返回值 (nMainRetVal) 傳遞給啟動函數。之后 exit() 便開始收尾工作。
? 運行時啟動函數調用操作系統的 ExitProcess 函數,將 nMainRetVal 傳遞給它,這使得操作系統能夠撤銷進程并設置它的 exit 代碼。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
