C#筆記 – CLR的執行模型(一) 托管模塊與元數據
公共語言運行時
-
公共語言運行時(Common Language Runtime,CLR)是一個可由多種編程語言使用的「運行時」
-
「運行時」!=「程序運行的時候」
-
「源代碼」-> 「中間碼」(IL)-> 「虛擬機」管理和執行IL
-
「虛擬機」可以參與和管理程序代碼的執行,解决了垃圾内存回收、安全性檢查等問題
- 這個「虛擬機」在.NET框架中就是「運行時」(公共語言運行時,CLR)
-
-
CLR的核心功能可由面向CLR的所有語言使用
- 如:內存管理、程序集加載、安全性、異常處理、線程同步
編譯源代碼的過程

托管模塊(managed module)
-
32位Microsoft Windows可移植執行體(PE32)文件 或 64位Microsoft Windows可移植執行體(PE32+)文件
- PE文件都需要CLR才能執行

-
PE32/PE32+頭:
-
Windows PE文件頭:
-
PE32文件能在32位/64位的Windows上運行
-
PE32+能在64位的Windows上運行
-
-
文件類型標識:如GUI、CUI、DLL、生成時間
-
與本機CPU代碼有關的信息(包含native CPU代碼時)
-
-
CLR頭
-
CLR頭包含模塊生成時所面向的CLR的major(主)和minor(次)版本號
-
托管模塊入口方法MethodDef元數據token
-
模塊的元數據、資源、強名稱、標志及其他的數據項的位置/大小,如:
-
指定模塊入口方法的MethodDef token
-
一個可選的強名稱數字簽名
-
模塊內部元數據表的大小和偏移量
-
具體格式可參考CorHdr.h頭文件定義的IMAGE_COR20_HEADER
-
該文件位於C:Program Files (x86)Windows KitsNETFXSDK4.8Includeum中
-
typedef struct IMAGE_COR20_HEADER { // Header versioning //CLR版本號 DWORD cb; WORD MajorRuntimeVersion; WORD MinorRuntimeVersion; // Symbol table and startup information //元數據表 IMAGE_DATA_DIRECTORY MetaData; //一些標志 DWORD Flags; //模塊的入口方法 union { DWORD EntryPointToken; DWORD EntryPointRVA; }; IMAGE_DATA_DIRECTORY Resources //強名稱簽名 IMAGE_DATA_DIRECTORY StrongNameSignature; IMAGE_DATA_DIRECTORY CodeManagerTable; // Depricated, not used IMAGE_DATA_DIRECTORY VTableFixups; IMAGE_DATA_DIRECTORY ExportAddressTableJumps; IMAGE_DATA_DIRECTORY ManagedNativeHeader; } IMAGE_COR20_HEADER, *PIMAGE_COR20_HEADER
-
-
-
-
元數據表
-
描述源代碼中定義的類型和成員的表
-
描述源代碼引用的類型和成員的表
-
-
IL(中間語言)代碼
-
編譯器編譯源代碼時生成
-
將被CLR編譯成本機CPU指令
-
-
編譯器與托管代碼
-
本機代碼編譯器(native code compilers)生成的是面向特定CPU架構(x86、x64、ARM)的代碼
-
面向CLR的編譯器(如Visual Studio內置的C#編譯器)生成的都是IL代碼
-
IL代碼的執行需要由CLR來管理,因此被稱為托管代碼
-
除了IL,還要在托管模塊中生成完整的元數據(metadata)
-
-
元數據
-
數據表的集合,包含了
-
定義表(definition table)
-
元數據定義表名稱 說明 ModuleDef - 對模塊進行標識的一個記錄項 - 該記錄項包含模塊文件名、擴展名(不包含路徑)以及模塊版本ID TypeDef - 模塊定義的每個類型 - 每個記錄項包含類型的: - 名稱 - 基類 - 標志(public、private等) - 一些索引,指向MethodDef表、FieldDef表、PropertyDef表、EventDef表下該類型的方法、字段、屬性、事件 MethodDef - 模塊定義的每個方法 - 包含了方法的名稱、標志(public、static、virtual等)、簽名、方法的IL代碼在模塊中的偏移量 - 每個記錄項還引用了ParamDef表中的一個記錄項,包含與該方法參數相關的信息 FieldDef - 模塊定義的每個字段,包含了字段標志(public、private等)、名稱和類型 ParamDef - 模塊定義的每個參數,包含了字段標志(in, out等)、名稱和類型 PropertyDef - 模塊定義的每個屬性,包含了標志、類型和名稱 EventDef - 模塊定義的每個事件,包含了標志和名稱 -
編譯器編譯源代碼時,代碼定義的任何東西都會導致上表列出的某個表中創建一個記錄項
-
-
引用表(reference table)
-
引用元數據表名稱 說明 AssemblyRef - 模塊引用的每個程序集,包含了綁定該程序集所需的信息 - 程序集名稱、版本號、語言文化、公鑰(token) - 每個記錄項還包含一些flag和一個hash值 ModuleRef - 實現該模塊所引用的類型的每個PE模塊 - 包含模塊的文件名和擴展名 - 可能是別的模塊實現了需要的類型,該表的作用就是建立與那些類型的綁定關係 TypeRef - 模塊引用的每個類型 - 包含類型的名稱和一個引用(指向類型的位置) - 如果類型在另一個類型中實現,引用指向一個TypeRef記錄項 - 如果在同一模塊中實現,指向ModuleDef - 如果在調用程序集的另一個模塊中實現,指向ModuleRef - 如果在不同的程序集中實現,指向AssemblyRef MemberRef - 模塊引用的每個成員(字段/方法/屬性/事件) - 包含成員的名稱和簽名,並指向對成員進行定義的那個類型的TypeRef記錄項 -
編譯器會檢測源代碼引用的類型、字段、方法、屬性、事件,並創建相應的記錄項。
-
-
清單表(manifest table)
-
清單元數據表名稱 說明 AssemblyDef - 如果模塊被標識為程序集,就會包含一個記錄項列出程序集名稱、版本、語言文化、flag、hash算法、發布者公鑰 FileDef - 作為程序集一部分的每個PE文件和資源文件都有一個記錄項(清單所在文件除外) - 包含文件名和擴展名、Hash值和一些flags - 如果程序集只包含它自己的文件,FileDef表將無記錄。(指程序集只包含它的主模塊,不包含其他非主模塊和資源文件) ManifestResourceDef - 作為程序集一部分的每個資源都有一個記錄項 - 包含資源名稱、一些flags、FileDef表的一個索引(指出資源或流包在哪個文件中) - 嵌入資源還包含一個偏移量,指出資源流在PE文件中的起始位置 ExportedTypesDef - 程序集的所有PE模塊導出的每個public類型都有一個記錄項 - 包含了類型名稱、FileDef表的一個索引(指出類型在哪個文件實現)、TypeDef表的一個索引(類型內信息) -
包含清單的程序集文件還有一個AssemblyRef表,程序集全部文件引用的每個程序集都有一個記錄項
- 只要打開程序集的清單,就可以知道它引用的全部程序集
-
-
-
元數據嵌入EXE/DLL文件中,與包含IL代碼的文件關聯
-
編譯器同時生成元數據和代碼,把它們綁定在一起,並嵌入最終生成的托管模塊
-
用途
-
實現類型/成員的IL代碼文件包含有關引用類型/成員的所有信息,編譯器直接從托管模塊讀取元數據,避免了編譯時對原生C/C++頭和庫文件的需求
-
CLR的代碼驗證過程使用元數據來確保只執行「類型安全」的操作
-
元數據允許將對象的字段序列化到內存塊中,然後在另一台機器上反序列化,在遠程機器上重建對象狀態
-
元數據允許GC跟蹤對象生存期:
- GC能判斷任何對象的類型,並從元數據中知道該對象的哪些字段引用了其他對象
-
-
我們可以使用Visual Studio提供的ILDasm.exe(IL Disassembler, IL反匯編器)檢查托管PE文件中的元數據。
- 在Visual Studio提供的命令行工具中執行 ildasm.exe [剛剛生成的可執行應用程序名],打開平常查看IL的窗口

- 再選擇「檢視」->「MetaInfo」->「顯示!」,打開查看應用程序的元信息
-
源代碼:
namespace CLR_Via_CSharp_4._0 { public sealed class Program { public static void Main(string[] args) { System.Console.WriteLine("Hello World!"); System.Console.ReadLine(); } } } -
元數據:
-
=========================================================== ScopeName : MyProject.exe MVID : {B0B1828F-8FEC-46CB-BB73-CCA8C0BD50CA} =========================================================== Global functions ------------------------------------------------------- Global fields ------------------------------------------------------- Global MemberRefs ------------------------------------------------------- TypeDef #1 (02000002) ------------------------------------------------------- TypDefName: CLR_Via_CSharp_4._0.Program (02000002) Flags : [Public] [AutoLayout] [Class] [Sealed] [AnsiClass] [BeforeFieldInit] (00100101) Extends : 01000005 [TypeRef] System.Object Method #1 (06000001) [ENTRYPOINT] ------------------------------------------------------- MethodName: Main (06000001) Flags : [Public] [Static] [HideBySig] [ReuseSlot] (00000096) RVA : 0x00002050 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] ReturnType: Void 1 Arguments Argument #1: SZArray String 1 Parameters (1) ParamToken : (08000001) Name : args flags: [none] (00000000) Method #2 (06000002) ------------------------------------------------------- MethodName: .ctor (06000002) Flags : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor] (00001886) RVA : 0x00002064 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. TypeRef #1 (01000001) ------------------------------------------------------- Token: 0x01000001 ResolutionScope: 0x23000001 TypeRefName: System.Runtime.CompilerServices.CompilationRelaxationsAttribute MemberRef #1 (0a000001) ------------------------------------------------------- Member: (0a000001) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: I4 TypeRef #2 (01000002) ------------------------------------------------------- Token: 0x01000002 ResolutionScope: 0x23000001 TypeRefName: System.Runtime.CompilerServices.RuntimeCompatibilityAttribute MemberRef #1 (0a000002) ------------------------------------------------------- Member: (0a000002) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. TypeRef #3 (01000003) ------------------------------------------------------- Token: 0x01000003 ResolutionScope: 0x23000001 TypeRefName: System.Diagnostics.DebuggableAttribute MemberRef #1 (0a000003) ------------------------------------------------------- Member: (0a000003) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: ValueClass DebuggingModes TypeRef #4 (01000004) ------------------------------------------------------- Token: 0x01000004 ResolutionScope: 0x01000003 TypeRefName: DebuggingModes TypeRef #5 (01000005) ------------------------------------------------------- Token: 0x01000005 ResolutionScope: 0x23000001 TypeRefName: System.Object MemberRef #1 (0a000006) ------------------------------------------------------- Member: (0a000006) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. TypeRef #6 (01000006) ------------------------------------------------------- Token: 0x01000006 ResolutionScope: 0x23000001 TypeRefName: System.Console MemberRef #1 (0a000004) ------------------------------------------------------- Member: (0a000004) WriteLine: CallCnvntn: [DEFAULT] ReturnType: Void 1 Arguments Argument #1: String MemberRef #2 (0a000005) ------------------------------------------------------- Member: (0a000005) ReadLine: CallCnvntn: [DEFAULT] ReturnType: String No arguments. Assembly ------------------------------------------------------- Token: 0x20000001 Name : MyProject Public Key : Hash Algorithm : 0x00008004 Version: 0.0.0.0 Major Version: 0x00000000 Minor Version: 0x00000000 Build Number: 0x00000000 Revision Number: 0x00000000 Locale: Flags : [none] (00000000) CustomAttribute #1 (0c000001) ------------------------------------------------------- CustomAttribute Type: 0a000001 CustomAttributeName: System.Runtime.CompilerServices.CompilationRelaxationsAttribute :: instance void .ctor(int32) Length: 8 Value : 01 00 08 00 00 00 00 00 > < ctor args: (8) CustomAttribute #2 (0c000002) ------------------------------------------------------- CustomAttribute Type: 0a000002 CustomAttributeName: System.Runtime.CompilerServices.RuntimeCompatibilityAttribute :: instance void .ctor() Length: 30 Value : 01 00 01 00 54 02 16 57 72 61 70 4e 6f 6e 45 78 > T WrapNonEx< : 63 65 70 74 69 6f 6e 54 68 72 6f 77 73 01 >ceptionThrows < ctor args: () CustomAttribute #3 (0c000003) ------------------------------------------------------- CustomAttribute Type: 0a000003 CustomAttributeName: System.Diagnostics.DebuggableAttribute :: instance void .ctor(value class DebuggingModes) Length: 8 Value : 01 00 07 01 00 00 00 00 > < ctor args: ( ) AssemblyRef #1 (23000001) ------------------------------------------------------- Token: 0x23000001 Public Key or Token: b7 7a 5c 56 19 34 e0 89 Name: mscorlib Version: 4.0.0.0 Major Version: 0x00000004 Minor Version: 0x00000000 Build Number: 0x00000000 Revision Number: 0x00000000 Locale: HashValue Blob: Flags: [none] (00000000) User Strings ------------------------------------------------------- 70000001 : (12) L"Hello World!" Coff symbol name overhead: 0 =========================================================== =========================================================== ===========================================================-
重點是以下幾個部分:
-
MyProject.exe包含名為「CLR_Via_CSharp_4._0.Program」的TypeDef
-
TypeDef #1 (02000002) ------------------------------------------------------- TypDefName: CLR_Via_CSharp_4._0.Program (02000002) Flags : [Public] [AutoLayout] [Class] [Sealed] [AnsiClass] [BeforeFieldInit] (00100101) Extends : 01000005 [TypeRef] System.Object Method #1 (06000001) [ENTRYPOINT] ------------------------------------------------------- MethodName: Main (06000001) ... Method #2 (06000002) ------------------------------------------------------- MethodName: .ctor (06000002) ... -
Program是public、sealed的(Flags)
-
Program從System.Object派出(Extends)
-
-
Program定義了兩個方法:Main和.ctor(構造器) => Method#1和Method#2
-
Method#1: Main
-
Method #1 (06000001) [ENTRYPOINT] ------------------------------------------------------- MethodName: Main (06000001) Flags : [Public] [Static] [HideBySig] [ReuseSlot] (00000096) RVA : 0x00002050 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] ReturnType: Void 1 Arguments Argument #1: SZArray String 1 Parameters (1) ParamToken : (08000001) Name : args flags: [none] (00000000) -
Main是public、static的方法(Flags)
-
用IL代碼實現(ImplFlags)
-
返回類型是Void(ReturnType)
-
有一個參數(1 Arguments)
-
-
Method#2:.ctor
-
Method #2 (06000002) ------------------------------------------------------- MethodName: .ctor (06000002) Flags : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor] (00001886) RVA : 0x00002064 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. -
.ctor(構造器)是public的(Flags)
-
用IL代碼實現(ImplFlags)
-
返回類型是Void(ReturnType)
-
無參(No arguments)
-
有一個this指針(hasThis) => 指向調用方法時構造對象的內存
-
-
-
-
-
-
「檢視」->「統計資料」中可以看到文件大小以及文件各部分大小(字節數和百分比)
-
File size : 3584PE header size : 512 (496 used) (14.29%)PE additional info : 1415 (39.48%)Num.of PE sections : 3CLR header size : 72 ( 2.01%)CLR meta-data size : 788 (21.99%)CLR additional info : 0 ( 0.00%)CLR method headers : 2 ( 0.06%)Managed code : 27 ( 0.75%)Data : 2048 (57.14%)Unaccounted : -1280 (-35.71%)Num.of PE sections : 3.text - 1024.rsrc - 1536.reloc - 512CLR meta-data size : 788Module - 1 (10 bytes)TypeDef - 2 (28 bytes) 0 interfaces, 0 explicit layoutTypeRef - 6 (36 bytes)MethodDef - 2 (28 bytes) 0 abstract, 0 native, 2 bodiesMemberRef - 6 (36 bytes)ParamDef - 1 (6 bytes)CustomAttribute- 3 (18 bytes)Assembly - 1 (22 bytes)AssemblyRef - 1 (20 bytes)Strings - 276 bytesBlobs - 92 bytesUserStrings - 28 bytesGuids - 16 bytesUncategorized - 172 bytesCLR method headers : 2Num.of method bodies - 2Num.of fat headers - 0Num.of tiny headers - 2Managed code : 27Ave method size - 13
-
-
參考書目
- 《CLR via C#》(第4版) Jeffrey Richter