自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

詳解CLR中Jit編譯發(fā)生的過程

開發(fā) 后端
這里將介紹CLR中Jit編譯發(fā)生的過程,通過筆者深入的探討,希望能對大家了解Jit的發(fā)生過程是怎么樣的有所了解。
理解在CLR中Jit編譯發(fā)生的過程,了解這個首先要從CLR談起。CLR(公共語言運行時,Common Language Runtime)和Java虛擬機(jī)一樣也是一個運行時環(huán)境,它負(fù)責(zé)資源管理(內(nèi)存分配和垃圾收集),并保證應(yīng)用和底層操作系統(tǒng)之間必要的分離。

CLR是如何找到托管代碼的入口方法并對其Jit的呢?Jit編譯的發(fā)生過程是怎么樣的呢?Jit編譯器和Metadata表又有什么關(guān)系呢?本文試圖尋找出答案,在此之前,不妨先了解一下CLR Header的大致結(jié)構(gòu)。

以如下代碼為例:

  1. Example  
  2. using System;  
  3.  
  4. namespace CLRTesing  
  5. {  
  6. class Program  
  7. {  
  8. static void Main(string[] args)  
  9. {  
  10. System.Console.WriteLine("Hello World!");  
  11. Console.ReadKey();  
  12.  
  13. new P().Display();  
  14.  
  15.  
  16. }  
  17.  
  18. Program()  
  19. {  
  20. Console.WriteLine("Constructor.");  
  21. Console.ReadKey();  
  22. }  
  23.  
  24. static Program()  
  25. {  
  26. Console.WriteLine("Static constructor.");  
  27. Console.ReadKey();  
  28. }  
  29. }  
  30.  
  31. class P  
  32. {  
  33. public void Display()  
  34. {  
  35. System.Console.WriteLine("P!");  
  36. Console.ReadKey();  
  37.  
  38. new Q().Display();  
  39. Console.ReadKey();  
  40. }  
  41. }  
  42.  
  43. class Q  
  44. {  
  45. public void Display()  
  46. {  
  47. System.Console.WriteLine("Q!");  
  48. Console.ReadKey();  
  49. }  
  50. }  

編譯后通過dumpbin工具的到其CLR Header,如圖所示:

dumpbin工具

從圖中可以看到,CLR Header由以下幾個部分組成:

1、CB:表示CLR Header的大小,單位是byte;

2、Run time version:運行時版本,包含兩部分MajorRuntimeVersion和MinorRuntimeVersion;

3、Metadata Directory:指出Metadata table的RVA和其大??;

4、Flag:這個標(biāo)識主要是供加載器使用,flag值為0x00000001表示當(dāng)前runtime image僅由IL代碼組成并且對CPU沒有特殊要求;值為0x00000002表示image只能被加載到32位機(jī)中,值為0x00010000表示運行時和jit編譯器需要追蹤方法的調(diào)試信息;

5、EntryPointToken:Metadata 表中標(biāo)記為EntryPoint的方法的MethodDef;

6、Resources Directory:CLR的資源,也就是托管資源的RVA和大小,注意與PE文件中存儲Win32資源的section不同;

7、StrongNameSignature Directory:PE文件中供CLR加載器使用的哈希值所處RVA和大?。?

8、CodeManagerTable Directory:Code Manager 表的RVA和其大小;

9、VTableFixups Directory:由非托管C++類型中虛方法的指針組成的數(shù)組;

10、ExportAddressTableJumps Directory:跳轉(zhuǎn)地址表的RVA和大??;

11、ManagedNativeHeader Directory:一般情況下為0。

以上結(jié)構(gòu)可以從CorHdr.h文件中看出,如果裝的是Visual Studio 2005,這個文件在\Microsoft Visual Studio 8\SDK\v2.0\include\。

查看托管PE文件的工具有很多,不用很復(fù)雜的,就園子里的大牛Anders Liu寫的CliPeViwer就很好用,用Reflector可以偷窺其代碼哦。

那么在上面這個結(jié)構(gòu)中我最關(guān)心的是Metadata directory和EntryPointToken,Metadata directory存提供了原數(shù)據(jù)所在內(nèi)存地址的范圍,EntryPointToken告訴我們在原數(shù)據(jù)表中哪個token標(biāo)識的方法是入口方法,這里一定是方法,所以這個token是以6開頭的一個數(shù)。

回到主題,我們從CLR已經(jīng)被載入內(nèi)存、mscorwks.dll中的_CorExeMain2方法接管主線程開始說起:

1、_CorExeMain2方法會調(diào)用System Domain中的SystemDomain::ExecuteMainMethod方法,然后由此方法再去調(diào)用其它方法(具體什么方法參見深入了解CLR的加載過程一文中的第8步), 通過MetaData表提供的接口查找包含.entrypoint的類型,接著返回入口方法(在C#中這個入口方法一定是Main方法)的一個MethodDesc類型的實例;獲取MethodDesc類型實例的這個過程我認(rèn)為是:CLR通過讀取MetaData表,定位入口方法所屬的類型,將包含該類型的Module載入,然后建立這個類型的EECLASS(EECLASS結(jié)構(gòu)中包含重要信息有:指向當(dāng)前類型父類的指針、指向方法表的指針、實例字段和靜態(tài)字段等)和這個類型所包含方法的Method Table(方法表由一個個Method Descripter組成,具體到內(nèi)存中就是指向若干MethodDesc類型實例的地址),通過EEClass::FindMethod方法找到并返回入口方法的MethodDesc類型實例。

MethodDesc這個類型很有意思,它有兩個重要的部分,一個部分叫做m_CodeOrIL,用來存儲編譯好的MSIL在內(nèi)存中的地址,初值為ffffffffffffffff,另一個部分叫做Stub,如果當(dāng)前代碼沒有被編譯為本地CPU指令,那么通過這個Stub會觸發(fā)對Jit編譯器的調(diào)用。

執(zhí)行上述代碼,

[[5737]]

用Windbg 查看,如下:

  1. Windbg1  
  2. 0:000> !name2ee *!CLRTesing.Program  
  3. Module: 790c2000 (mscorlib.dll)  
  4. --------------------------------------  
  5. Module: 00a72c3c (Hello.exe)  
  6. Token: 0x02000002  
  7. MethodTable: 00a73048  
  8. EEClass: 00a7129c  
  9. Name: CLRTesing.Program  
  10.  
  11. 0:000> !name2ee *!CLRTesing.P  
  12. Module: 790c2000 (mscorlib.dll)  
  13. --------------------------------------  
  14. Module: 00a72c3c (Hello.exe)  
  15. Token: 0x02000003  
  16. MethodTable:   
  17. EEClass:   
  18. Name: CLRTesing.P  
  19.  
  20. 0:000> !dumpmt -md 00a73048  
  21. EEClass: 00a7129c  
  22. Module: 00a72c3c  
  23. Name: CLRTesing.Program  
  24. mdToken: 02000002(D:\test\Hello\bin\Debug\Hello.exe)  
  25. BaseSize: 0xc  
  26. ComponentSize: 0x0  
  27. Number of IFaces in IFaceMap: 0  
  28. Slots in VTable: 7  
  29. --------------------------------------  
  30. MethodDesc Table  
  31.  Entry MethodDescJIT Name  
  32. 79371278 7914b928 PreJIT System.Object.ToString()  
  33. 7936b3b0 7914b930 PreJIT System.Object.Equals(System.Object)  
  34. 7936b3d0 7914b948 PreJIT System.Object.GetHashCode()  
  35. 793624d0 7914b950 PreJIT System.Object.Finalize()  
  36. 00a7c011 00a73030 NONE CLRTesing.Program.Main(System.String[])  
  37. 00a7c015 00a73038 NONE CLRTesing.Program..ctor()  
  38. 00da0070 00a73040JIT CLRTesing.Program..cctor() 

CLRTesing.Program類型的靜態(tài)構(gòu)造函數(shù)執(zhí)行時,入口方法Main和CLRTesing.Program的實例構(gòu)造函數(shù)還沒有被Jit,Main方法中引用到的CLRTesing.P類型也沒有被加載,所以它的Method Table和EEClass結(jié)構(gòu)也沒有建立起來。

#p#

2、在Windbg中detach debuggee,隨便敲一個字符讓程序繼續(xù)運行;接著,入口方法Main開始執(zhí)行,

入口方法Main開始執(zhí)行

因為Main方法第一次執(zhí)行,所以通過Stub,Jit編譯器會被喚起,由于Main方法引用了CLRTesing.P類型,那么在執(zhí)行前會將CLRTesing.P類型載入,并建立Method Table和其EEClass結(jié)構(gòu),當(dāng)然這個建立過程也要去查找MetaData表,我認(rèn)為這個過程是這樣的:

Main方法被調(diào)用,由于它沒有被Jit過,CLR會通過Main方法的MethodDesc結(jié)構(gòu)的Stub對Jit編譯器進(jìn)行調(diào)用,CLR通過MetaData表的接口找到Main方法對應(yīng)的Token,如下:

Jit編譯

我們可以看到Main方法的RVA是0x00002050,于是去PE文件的.Text section中的Raw Data中查找image base+RVA這個位置處的IL代碼,接著Jit編譯器會對這段IL代碼進(jìn)行驗證,驗證過程通過調(diào)用CheckIL方法來實現(xiàn),這個方法的簽名可以是這樣的:

  1. CHECK CheckIL(RVA il);  
  2. CHECK CheckIL(RVA il, COUNT_T size); 

驗證結(jié)束后把這段IL代碼編譯成本地CPU指令,將編譯后后的CPU指令存到內(nèi)存并修改Main方法的MethodDesc結(jié)構(gòu)中m_CodeOrIL和Stub的值,讓它們指向這個新的內(nèi)存地址,當(dāng)這個方法被再次調(diào)用的時候就會直接通過這個地址訪問到本地CPU指令而不會觸發(fā)第二次編譯。對于這個過程大家的看法呢?用Windbg查看各對象情況:

  1. Windbg2  
  2. 0:000> !name2ee *!CLRTesing.Program  
  3. Module: 790c2000 (mscorlib.dll)  
  4. --------------------------------------  
  5. Module: 00a72c3c (Hello.exe)  
  6. Token: 0x02000002  
  7. MethodTable: 00a73048  
  8. EEClass: 00a7129c  
  9. Name: CLRTesing.Program  
  10.  
  11. 0:000> !name2ee *!CLRTesing.P  
  12. Module: 790c2000 (mscorlib.dll)  
  13. --------------------------------------  
  14. Module: 00a72c3c (Hello.exe)  
  15. Token: 0x02000003  
  16. MethodTable: 00a730b8  
  17. EEClass: 00a71730  
  18. Name: CLRTesing.P  
  19.  
  20. 0:000> !name2ee *!CLRTesing.Q  
  21. Module: 790c2000 (mscorlib.dll)  
  22. --------------------------------------  
  23. Module: 00a72c3c (Hello.exe)  
  24. Token: 0x02000004  
  25. MethodTable:   
  26. EEClass:   
  27. Name: CLRTesing.Q  
  28.  
  29. 0:000> !dumpmt -md 00a73048  
  30. EEClass: 00a7129c  
  31. Module: 00a72c3c  
  32. Name: CLRTesing.Program  
  33. mdToken: 02000002(D:\test\Hello\bin\Debug\Hello.exe)  
  34. BaseSize: 0xc  
  35. ComponentSize: 0x0  
  36. Number of IFaces in IFaceMap: 0  
  37. Slots in VTable: 7  
  38. --------------------------------------  
  39. MethodDesc Table  
  40.  Entry MethodDescJIT Name  
  41. 79371278 7914b928 PreJIT System.Object.ToString()  
  42. 7936b3b0 7914b930 PreJIT System.Object.Equals(System.Object)  
  43. 7936b3d0 7914b948 PreJIT System.Object.GetHashCode()  
  44. 793624d0 7914b950 PreJIT System.Object.Finalize()  
  45. 00da00b0 00a73030JIT CLRTesing.Program.Main(System.String[])  
  46. 00a7c015 00a73038 NONE CLRTesing.Program..ctor()  
  47. 00da0070 00a73040JIT CLRTesing.Program..cctor()  
  48.  
  49. 0:000> !dumpmt -md 00a730b8  
  50. EEClass: 00a71730  
  51. Module: 00a72c3c  
  52. Name: CLRTesing.P  
  53. mdToken: 02000003(D:\test\Hello\bin\Debug\Hello.exe)  
  54. BaseSize: 0xc  
  55. ComponentSize: 0x0  
  56. Number of IFaces in IFaceMap: 0  
  57. Slots in VTable: 6  
  58. --------------------------------------  
  59. MethodDesc Table  
  60.  Entry MethodDescJIT Name  
  61. 79371278 7914b928 PreJIT System.Object.ToString()  
  62. 7936b3b0 7914b930 PreJIT System.Object.Equals(System.Object)  
  63. 7936b3d0 7914b948 PreJIT System.Object.GetHashCode()  
  64. 793624d0 7914b950 PreJIT System.Object.Finalize()  
  65. 00a7c04c 00a730a8 NONE CLRTesing.P.Display()  
  66. 00a7c058 00a730b0 NONE CLRTesing.P..ctor() 

我們可以發(fā)現(xiàn)Main方法已經(jīng)被Jit,且它引用的CLRTesing.P類型的相關(guān)結(jié)構(gòu)也已經(jīng)建立起來了,而CLRTesing.P類型的Display方法所引用的CLRTesing.Q類型沒有被載入。

總結(jié)一下,Jit編譯針對的對象總是方法,不論是入口方法還是其他方法的Jit過程都類似上述過程,Metadata這這里的作用不言而喻,可以說沒有Metadata的支持就無法進(jìn)行Jit,我覺得Meatadata在Jit編譯期間的作用至少有三個:

1、Jit編譯器通過查找Metadata來找到入口方法;

2、Jit編譯器通過查找Metadata來定位待編譯方法并利用其RVA找到存儲于PE文件中的IL代碼在內(nèi)存中的實際地址;

3、Jit編譯器在找到IL代碼并準(zhǔn)備編譯為本地CPU指令前所進(jìn)行的IL代碼驗證同樣會用到Metadata,例如,驗證方法的合法性需要去核實方法參數(shù)數(shù)量是正確的、傳給方法的每個參數(shù)是否都有正確的類型、方法返回值是否正確等等。

文中是一些我通過Shared Source Common Language Infrastructure(SSCLI)看到的和感覺到的東西,希望能給大家理解Jit提供一點幫助,如果有錯誤的地方也請大家指出,大家一起學(xué)習(xí)。

最后要說明的是,SSCLI里東西僅作為理解CLR使用,與MS真正實現(xiàn)CLR的過程可能不一樣。最后,大家在看SSCLI的時候可以使用Source Insight,個人感覺還挺好用。

 SSCLI的下載地址是:http://www.microsoft.com/downloads/details.aspx?FamilyId=8C09FD61-3F26-4555-AE17-3121B4F51D4D&displaylang=en。

本文來自Leo Zhang博客園文章《深入了解Jit編譯發(fā)生的過程

【編輯推薦】

  1. C#treeview遞歸操作數(shù)據(jù)庫淺析
  2. C#遞歸樹實現(xiàn)實例簡析
  3. C#打開記事本實現(xiàn)實例解析
  4. C#調(diào)用記事本實例淺析
  5. C#日期格式化方法簡析
責(zé)任編輯:彭凡 來源: 博客園
相關(guān)推薦

2009-10-23 09:36:25

.Net Compac

2009-08-24 11:36:27

CLR加載過程

2023-10-31 11:46:32

編譯器托管CLR

2022-05-18 07:58:21

Linux程序編譯代碼

2020-11-09 14:41:58

iOS 14.2蘋果JIT

2009-09-18 10:40:05

CLR存儲過程

2024-11-27 16:25:54

JVMJIT編譯機(jī)制

2009-09-18 14:09:57

SQL CLR存儲過程

2009-10-22 14:05:55

CLR存儲過程

2009-10-22 13:02:47

SQL CLR存儲過程

2009-10-22 18:06:31

CLR存儲過程

2009-09-17 19:19:17

CLR存儲過程

2011-08-17 17:29:32

Windows編譯MySQL

2009-09-18 10:55:17

CLR存儲過程

2009-10-19 14:25:16

靜態(tài)構(gòu)造函數(shù)

2009-03-11 10:29:23

代碼契約.NETCLR

2011-05-03 10:31:59

噴墨打印機(jī)注墨誤區(qū)

2009-10-22 15:09:40

CLR存儲過程

2022-04-10 10:57:06

eBPFJIT即時編譯

2009-05-15 09:33:52

開發(fā)線程沖突lock
點贊
收藏

51CTO技術(shù)棧公眾號