天天看點

[CLR via C#]1.4 執行程式集的代碼

1. 托管程式集同時包含中繼資料和IL。IL是與CPU無關的機器語言。可将IL是為一種面向對象的機器語言。

2. IL也是能使用彙編語言來寫的,MicroSoft專門提供了一個名為ILAsm.exe的IL彙編器和一個名為ILDasm.exe的IL反彙編。

3. 進階語言隻公開了CLR的所有功能的一個子集,IL彙編語言允許開發人員通路CLR的所有功能。如果你需要目前使用的語言不支援的CLR功能,可以使用IL語言或者其他CLR語言。

4. 為了執行一個方法,首先必須将它的IL轉換成為本地CPU指令,這是CLR的JIT(just-in-time或"即時")編譯器的職責。

5. 展示一個方法首次調用發生的事情。

①在Main方法執行之前,CLR會檢測出Main的代碼引用的所有類型。這會使CLR配置設定一個内部資料結構,用于管理對所引用的類型的通路。

圖1-4中,Main方法引用了一個Console類型(或就叫做Console類),這将讓CLR配置設定一個内部結構。在這個結構中,Console類型定義的每個方法都有一個相對應的記錄項。每一個記錄項都容納一個位址(但目前還是沒有的,還沒到這一步),根據位址即可找到方法的實作。

②初始化CLR配置設定了一個内部結構,CLR将每個記錄項都設定成包含在CLR内部的一個未文檔化的函數(就了解成未公開的,隻有微軟自己清楚的函數)。姑且就将這個函數命名為JITCompiler(MSDN找不到這個函數,為了說明流程,自己取的函數名,因為真正的函數名微軟沒公開)

③Main方法首次調用WriteLine時,JITCompiler也就被調用了。JIT函數負責将一個方法的IL代碼編譯成本地CPU指令。由于IL是"即時"編譯的,所有通常将這個元件成為JIT編譯器或JITter。

④JITCompiler函數被調用時,它知道要調用的是哪個方法,以及具體是什麼類定義了該方法。于是乎,JITCompiler會在定義該類型的程式集的中繼資料中查找被調用的方法的IL。

⑤接着就是驗證IL代碼,并将IL編譯成為本地CPU指令。本地CPU指令被儲存到了一個動态配置設定的記憶體塊中。

⑥然後,JITCompiler在CLR為類型建立的内部資料結構,找到與被調用的方法對應的那一條記錄項,修改最初對JITCompiler的引用,讓它現在指向記憶體塊(其中包括了剛才編譯好的本地CPU指令)的位址。

⑦最後,JITCompiler函數跳轉到記憶體塊中的代碼,繼續執行裡面的具體的功能代碼,這些代碼執行完後,會傳回到Main中,并像往常一樣繼續執行。

[CLR via C#]1.4 執行程式集的代碼

⑧現在,Main要執行第二個WriteLine方法了。這一次,由于第一次已對WriteLine的代碼進行了驗證和編譯,是以會直接執行記憶體塊中的代碼,完全跳過JITCompiler函數。第二個WriteLine方法執行完畢,會再次傳回Main。圖1-5展示了第二次調用WriteLine時發生的事。

[CLR via C#]1.4 執行程式集的代碼

6. 對于大多數應用程式,因JIT編譯造成的性能損失并不顯著。大多數引用程式會反複調用相同的方法。看到上面,你對.NET的“第一次”是否有了颠覆性的認識了。

7. CLR的JIT編譯器會對本地代碼進行優化,代碼優化後會獲得更出色的性能。

9. IL是基于棧的。這就意味着它的所有執行都要将操作數壓入(push)一個執行棧,并處棧彈出(pop)結果。

10. IL提供的最大優勢在于應用程式的健壯性和安全性。将IL編譯成CPU指令時,CLR會執行一個名為驗證(verfication)的過程。這個過程會檢查進階IL代碼,确定代碼所做的一切都是安全的。

11. C#編譯器預設生成的是安全(safe)代碼,這種代碼是否安全是可驗證的。然而,C#編譯器也允許開發人員寫不安全(unsafe)代碼。

12. 不安全代碼允許直接操作記憶體位址,并可操作這些位址處的位元組,通常隻有在與非托管代碼進行互操作,或在提升效率極高的一個算法的性能時,才會這麼做。

13.  MicroSoft提供一個名為PEverify.exe的好、程式,它檢查一個程式集的所有方法,并報告其中含有不安全代碼的方法。

繼續閱讀