Windows系統中,.exe后綴的文件一般可以雙擊運行。編程時(shí),編譯出來(lái)的最終結果一般也表現為一個(gè)exe程序和其他的為程序執行提供支持的dll。我們雙擊一個(gè)exe程序的時(shí)候,在操作系統層面上,做了些什么使得應用程序能夠執行呢?
現在有一個(gè)App.exe文件,根據這篇文章的說(shuō)法,我總結了一下,雙擊App.exe之后操作系統做的工作如下:
1、 shell調用CreateProcss激活一個(gè)App.exe進(jìn)程。Shell即命令解釋器,是操作系統引導時(shí)即加載的一個(gè)系統進(jìn)程,在Windows任務(wù)管理器里面可以看到一個(gè)名為”Explorer.exe”的進(jìn)程,就是它了。
2、 CreateProcss創(chuàng )建了一個(gè)進(jìn)程內核對象,而系統為該進(jìn)程創(chuàng )建4GB的虛擬地址空間(在Win2000/WinXP下,每個(gè)進(jìn)程可以有2GB的私有地址空間,剩余的2GB由操作系統占用)用來(lái)加載App.exe和其他必要的DLL函數;
3、 CreateProcess加載exe文件,分析文件頭(具體格式見(jiàn)PE文件格式分析)以識別文件的運行環(huán)境,根據文件頭決定由那個(gè)環(huán)境進(jìn)行加載操作;
4、 加載App.exe及其必要的DLL文件數據和代碼后,CreateProcss即創(chuàng )建主線(xiàn)程,執行C/C++運行時(shí)的啟動(dòng)代碼,由啟動(dòng)代碼執行剩下的過(guò)程。
從上面的描述可以看出,一個(gè)程序真正調用的第一個(gè)應該是C/C++運行時(shí)的啟動(dòng)函數。那么C/C++運行時(shí)庫在程序運行時(shí)起到了什么樣的作用?下面是關(guān)于C/C++運行時(shí)的一些學(xué)習體會(huì )。
什么是C/C++運行時(shí)庫,網(wǎng)上隨便一搜,能得到一大串結果。運行時(shí)庫是一個(gè)library,我們日常編寫(xiě)的程序代碼都是運行在這個(gè)庫上的,運行時(shí)庫完成了一些底層的基礎的工作,例如初始化運行期間的內存單元分配函數,初始化底層I/O例程使用的內存棧,初始化C/C++運行時(shí)的全局變量,為C++全局和靜態(tài)類(lèi)調用構造函數等等。這樣的運行時(shí)庫使得程序員不必關(guān)心過(guò)于底層的內容,專(zhuān)注于自己的應用程序邏輯。運行時(shí)庫還提供一些基礎的庫函數調用,如memcpy,malloc之類(lèi)的,更重要的是,運行時(shí)庫還為應用程序添加啟動(dòng)函數。
Windows環(huán)境下,VC提供的 C run-time library又分為動(dòng)態(tài)運行時(shí)庫和靜態(tài)運行時(shí)庫。動(dòng)態(tài)運行時(shí)庫主要是msvcrt.dll(Debug版:msvcrtd.dll),對應的庫文件是msvcrt.lib(Debug版:msvcrtd.lib)。靜態(tài)運行時(shí)庫對應的主要文件是:libc.lib (單線(xiàn)程靜態(tài)庫)和libcmt.lib (多線(xiàn)程靜態(tài)庫)。其中msvcrt.dll提供幾千個(gè)C函數,包括printf這么低級的函數也在msvcrt.dll中。
應用程序編寫(xiě)完成后進(jìn)行編譯和鏈接時(shí),編譯器根據編譯選項(Visual Studio中即為工程設置),如單線(xiàn)程、多線(xiàn)程或DLL,自動(dòng)為應用程序鏈接不同的運行時(shí)庫的啟動(dòng)函數。在VS2005中,通過(guò)下面的操作可以查看或修改選項以決定鏈接哪個(gè)運行時(shí)庫:
打開(kāi)工程屬性,選擇左側的配置屬性—->C/C++—->代碼生成,查看【運行時(shí)庫】,選擇不同的運行時(shí)庫即可。
回到第一個(gè)問(wèn)題,主線(xiàn)程執行C/C++運行時(shí)的啟動(dòng)代碼,由啟動(dòng)函數調用對應的入口點(diǎn)函數,進(jìn)入應用程序執行代碼邏輯。
不使用寬字節的控制臺程序的啟動(dòng)函數為mainCRTStartup。這個(gè)函數在VC安裝目錄下的crt\src\ crt0.c文件中。下面是一個(gè)從網(wǎng)上找到的簡(jiǎn)化版:
void mainCRTStartup(void)
{
int mainret;
/*獲得WIN32完整的版本信息*/
_osver = GetVersion();
_winminor = ( _osver >> 8 ) & 0×00FF ;
_winmajor = _osver & 0×00FF ;
_winver = ( _winmajor << 8 ) + _winminor;
_osver = ( _osver >> 16 ) & 0×00FFFF ;
_ioinit(); /* initialize lowio */
/* 獲得命令行信息 */
_acmdln = (char *) GetCommandLineA();
/* 獲得環(huán)境信息 */
_aenvptr = (char *) __crtGetEnvironmentStringsA();
_setargv(); /* 設置命令行參數 */
_setenvp(); /* 設置環(huán)境參數 */
_cinit(); /* C數據初始化:全局變量初始化,就在這里!*/
__initenv = _environ;
mainret = main( __argc, __argv, _environ ); /*調用main函數*/
exit( mainret );
}
從以上代碼可知,運行庫在調用用戶(hù)程序的main或WinMain函數之前,進(jìn)行了一些初始化工作。初始化完成后,接著(zhù)才調用了我們自己編寫(xiě)的main或WinMain函數。這樣, C/C++運行時(shí)庫和應用程序就正常地工作起來(lái)了。
除了crt0.c外,C運行時(shí)庫中還包含wcrt0.c、 wincrt0.c、wwincrt0.c三個(gè)文件用來(lái)提供初始化函數。wcrt0.c是crt0.c的寬字符集版,wincrt0.c中包含 windows應用程序的入口函數,而wwincrt0.c則是wincrt0.c的寬字符集版。
同樣由上面簡(jiǎn)化版的代碼可知,當用戶(hù)程序的main或WinMain函數執行結束后,返回值被當做參數傳入exit函數中,有exit完成程序執行的收尾工作,包括析構C++全局和靜態(tài)類(lèi),調用操作系統的ExitProcess函數,告知進(jìn)程退出等。
以上是我對于程序執行過(guò)程和C/C++運行時(shí)庫的一點(diǎn)理解,若有錯漏,歡迎指正。
原帖:http://www.cnblogs.com/BpLoveGcy/archive/2010/03/24/1694240.html
聯(lián)系客服