|
對(duì)于一個(gè)已編譯好的.NET程序集,Windows操作系統(tǒng)是如何啟動(dòng)執(zhí)行的呢?日常使用中我們發(fā)現(xiàn)對(duì)于托管的和非托管的程序集編譯器都會(huì)吧程序集編譯成以.exe或.dll等為擴(kuò)展名的文件,可見Windows加載器并沒有區(qū)分是托管還是非托管的程序集,而且我們也知道對(duì)非托管的程序集是在編譯器直接編譯成了機(jī)器碼,自然可以由CPU直接執(zhí)行,而托管的.NET 程序集是包含復(fù)雜結(jié)構(gòu)的MSIL代碼,執(zhí)行時(shí)會(huì)使用JIT即時(shí)編譯器將IL代碼編譯成機(jī)器碼,再由CPU執(zhí)行,當(dāng)然這期間還需要執(zhí)行其它許多的工作,如加載CLR、執(zhí)行初始化等工作,那么這些是怎么自動(dòng)實(shí)現(xiàn)的呢?
理解這些問題是我們深入.NET的關(guān)鍵,由于日常的開發(fā)工作并不涉及這些知識(shí)(編譯器已經(jīng)給我們做了),結(jié)果是很多的.NET 書忽略了這一點(diǎn),也很少有人研究者方面的內(nèi)容,所以在閱讀了《.NET 高級(jí)調(diào)試》這本書給了我非常清晰地認(rèn)識(shí),把里面的這方面精彩的內(nèi)容做了一個(gè)總結(jié)以供那些還沒有清楚.NET 程序集如何執(zhí)行的學(xué)者們一個(gè)簡(jiǎn)單的參考。
首先我們要清楚的是對(duì)于托管還是非托管程序集,他們?cè)诰幾g器執(zhí)行編譯時(shí)都會(huì)編譯成一個(gè)特殊的文件格式,即PE文件(可移植可執(zhí)行文件格式),操作系統(tǒng)加載器通過加載這樣的PE文件來執(zhí)行程序集的。可以這么說吧,無論是托管程序還是非托管程序他們實(shí)際上都是編譯成這樣的PE文件(只是有部分內(nèi)容不一樣而已)。
然后這個(gè)PE文件會(huì)指示如何執(zhí)行托管程序集和非托管程序集,加載器首先會(huì)查找到PE頭中的AddressOfEntryPoint域,這個(gè)域指示PE文件的入口點(diǎn)位置,在.NET程序集中是指向.text段中的CLR頭--〉包含一個(gè)結(jié)構(gòu)IMAGE_COR20_HEADER—包含許多信息如托管代碼應(yīng)用程序的入口點(diǎn),目標(biāo)CLR的主版本號(hào)和從版本號(hào),以及程序集的強(qiáng)名稱簽名等--〉Windows加載器根據(jù)這個(gè)數(shù)據(jù)結(jié)構(gòu)決定加載哪個(gè)版本的CLR以及一些基本的程序集信息。在.text段中還包含了程序集的元數(shù)據(jù)表,MSIL以及非托管啟動(dòng)存根代碼,而非托管啟動(dòng)存根代碼包好了由Windows加載器執(zhí)行役啟動(dòng)PE文件執(zhí)行的代碼,結(jié)構(gòu)如圖所示。
這樣.NET 程序集的加載算法包括:
1、用戶執(zhí)行一個(gè).NET程序集;
2、Windows加載器查看AddressOfEntryPoint域,并找到PE映像文件的.text段;
3、位于AddressOfEntryPoint位置上的字節(jié)只是一個(gè)JMP(跳轉(zhuǎn))指令,這個(gè)指令跳轉(zhuǎn)到mscoree.dll中的一個(gè)導(dǎo)入函數(shù);
4、將執(zhí)行控制轉(zhuǎn)移到mscoree.dll中的_CorExeMain中,這個(gè)函數(shù)將啟動(dòng)CLR并把執(zhí)行控制轉(zhuǎn)移到程序集的入口點(diǎn)。
NET技術(shù):理解.NET程序集的執(zhí)行過程,轉(zhuǎn)載需保留來源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。