崇信县门户网站最新留言,如何建立公司网页,用户等待网站速度,网吧设计公司说在#xff0c;开篇之前很早就有说说Metadata#xff08;元数据#xff09;和IL#xff08;中间语言#xff09;的想法了#xff0c;一直在这篇开始才算脚踏实地的对这两个阶级兄弟投去些细关怀#xff0c;虽然来得没有《第一回#xff1a;恩怨情仇#xff1a;is和as…说在开篇之前很早就有说说Metadata元数据和IL中间语言的想法了一直在这篇开始才算脚踏实地的对这两个阶级兄弟投去些细关怀虽然来得没有《第一回恩怨情仇is和as》那么迅速但是Metadata和IL却是绝对重量级的内容值得我们在任何时间关注本文就是开始。 www.anytao.com
1 引言
你可曾想到我们的C#代码编译之后究竟为何物你可曾认知我们的可执行程序运行之时的轨迹究竟为哪般那么本文通过对Metadata元数据和ILIntermediate Language, 中间语言的认识开始来逐步给出答案。在这个探索轨迹上元数据、IL、程序集、程序域、JIT、虚分派、方法表和托管堆这些形形色色的神秘嘉宾将在某个时刻不期而遇作为你必须知道的.NET 系列2.0版本的一部分本文首先从认识元数据和IL这两位重量级选手开始而其他的嘉宾也将很快登场。
2 初次接触
在事实上编译之后的cs代码被组织为两种基本的元素元数据Metadata和IL。我们可以以最简单的方式来了解程序集*.dll或可执行文件*.exe中包含的Metadata和IL的秘密这种方式就是我们常说的反编译打开ILDasm并加载实现准备的程序集我们可以看到托管PE文件的相关内容 详细的结构信息和IL代码分析可以参见[你必须知道的.NET]第3章 “一切从IL开始”的介绍在此就不做太多的分析。另外我们可以通过执行“View/MetaInfo/Show!”或者CtrlM快捷键来获取该程序集所使用的MetaData信息列表 其中该程序集使用的元数据主要有Module、TypeRef、TypeDef、Method、Param、MemberRef、CostomAttribute、Assembly、AssemblyRef等同时还包括#Strings、#GUID、#Blob、#US堆等。
当然关于ILDasm工具还有很多好玩的使用方式来满足我们探索IL代码的好奇心例如 ildasm Anytao.Insidenet.MetadataIL.exe /output:my.il将反编译结果导出为il代码格式生成一个my.il包含了所有的IL代码和一个my.res包含了所有的资源文件。 ildasm Anytao.Insidenet.MetadataIL.exe /text将反编译结果以Console形式输出。 当然我们还是推荐以GUI形式来查看IL细节组织结构良好的Class View ildasm Anytao.Insidenet.MetadataIL.exe 下面首先给出参与编译的相关代码文件然后再展开我们对Metadata和IL的讨论 // Release : code01, 2009/02/12 // Author : Anytao, http://www.anytao.com // List : One.cs public class One { public int ID { get; set; } }// Release : code02, 2009/02/12 // Author : Anytao, http://www.anytao.com // List : Two.cs public class Two { public string SayHello() { return Hello, world.; } }// Release : code03, 2009/02/12 // Author : Anytao, http://www.anytao.com // List : Program.cs class Program { static void Main(string[] args) { int id 1; One one new One(); one.ID id; Two two new Two(); Console.WriteLine(two.SayHello()); } } 接着我们对上述程序的编译执行过程进行一点探索以命令行编译器来演化其大致的编译过程以此进一步了解托管模块程序集和可执行文件之间的关系 打开Visual Studio 2008 Command Prompt并定位到cs代码所在文件夹编译One.cs为托管模块执行命令 csc /t:module One.cs
执行之后将生成名为One.netmodule文件 继续执行将多个模块打包为程序集 csc /t:library /addmodule:One.netmodule Two.cs 执行之后将生成名为Two.dll文件 最后编译Main函数和Two.dll为可执行文件 csc /out:Anytao.Insidenet.MetatdataIL.exe /t:exe /r:Two.dll /r:mscorlib.dll Program.cs 最终将得到本文开始时所加载的用于反编译的程序集文件Anytao.Insidenet.MetadataIL.exe在该执行命令中对几个指示符开关做点说明
/out:Anytao.Insidenet.MetadataIL.exe表示输出的可执行文件及其名称/t:exe表示输出的文件类型为CUI控制台界面程序程序而/t:winexe表示输出为GUI图形界面程序程序/r:Two.dll表示引用刚刚生产的Two.dll程序集/r:mscorlib.dll表示因为外部程序集mscorlib.dll因为我们的程序中使用了Console静态方法而该方法则被定义在mscorlib.dll中。mscorlib.dll是如此的重要我们将在本文之后的某些时候再次与mscorlib.dll握手那时在对其进行一个详细的分析敬请期待。
在cmd中的执行过程可以参考 通过分步执行的方式我们对csc编译器的执行过程有个基本的了解也同时从侧面认识了每次在Visual Studio中执行“Build“或者“ReBuild”的缩影。综上分析我们可以简单的看到 Note在Visual Studio中编译是分模块进行的编译结果保存在obj目录中最后再合并为可执行文件于bin目录同时默认情况下编译过程是增量式的仅编译发生修改的模块我将在后文给出较为详细的过程。
同时我们还可以收获以下几个基本的结论 cs代码编译之后将生成元数据和IL并组成托管模块Module的基本单元。 多个托管模块组成程序集其实还包括一定的资源文件只是没有在此体现。 程序集或者可执行文件是逻辑组织的基本单元符合基本的Windows PE文件格式可以被x86或者x64Windows直接加载执行。
3 继续深入
一个或者多个模块再加上资源文件就形成了程序集Assembly作为逻辑组织的基本单元 事实上此图仅仅从粗粒度对程序集的基本组成有个大致的了解实际上程序集中包含了复杂的结构和要素例如PE Signature、Managed Resources、Strong Name Signature Hash而其中最核心的要素则体现在上图。
程序集清单MANIFEST包含了程序集的自描述信息主要包含AssemblyDef、FileDef、ManifestResourceDef和ExportedTypeDef在反编译选项中MANIFEST包含了详细的内容。在《你必须知道的.NET》3.1节 “从Helloworld开始认识IL”对其有过详细的描述此不赘述。PE文件头标准Windows PE头文件PE32或PE32PE文件的基本信息例如文件类型创建时间本地CPU信息等。CLR头包含CLR版本、模块元数据、资源等信息。资源文件。
执行View/Statisctics菜单可以打开相关的统计信息 File size : 5632 PE header size : 512 (496 used) ( 9.09%) PE additional info : 1691 (30.02%) Num.of PE sections : 3 CLR header size : 72 ( 1.28%) CLR meta-data size : 2212 (39.28%) CLR additional info : 0 ( 0.00%) CLR method headers : 52 ( 0.92%) Managed code : 287 ( 5.10%) Data : 2048 (36.36%) Unaccounted : -1242 (-22.05%) Num.of PE sections : 3 .text - 3072 .rsrc - 1536 .reloc - 512 CLR meta-data size : 2212 Module - 1 (10 bytes) TypeDef - 4 (56 bytes) 0 interfaces, 0 explicit layout TypeRef - 25 (150 bytes) MethodDef - 8 (112 bytes) 0 abstract, 0 native, 8 bodies FieldDef - 1 (6 bytes) 0 constant MemberRef - 29 (174 bytes) ParamDef - 2 (12 bytes) CustomAttribute- 16 (96 bytes) StandAloneSig - 4 (8 bytes) PropertyMap - 1 (4 bytes) Property - 1 (6 bytes) MethodSemantic- 2 (12 bytes) Assembly - 1 (22 bytes) AssemblyRef - 1 (20 bytes) Strings - 920 bytes Blobs - 328 bytes UserStrings - 68 bytes Guids - 16 bytes Uncategorized - 192 bytes CLR method headers : 52 Num.of method bodies - 8 Num.of fat headers - 4 Num.of tiny headers - 4 Managed code : 287 Ave method size - 35 我们将在后篇《深入程序集和模块》中对PE头CLR头和资源文件进行详细论述。
IL代码被组织为.class public auto ansi beforefieldinit Anytao.Insidenet.MetadataIL.Two extends [mscorlib]System.Object { .method public hidebysig instance string SayHello() cil managed { // Code size 11 (0xb) .maxstack 1 .locals init ([0] string CS$1$0000) IL_0000: nop IL_0001: ldstr Hello, world. IL_0006: stloc.0 IL_0007: br.s IL_0009 IL_0009: ldloc.0 IL_000a: ret } // end of method Two::SayHello .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method Two::.ctor } // end of class Anytao.Insidenet.MetadataIL.Two 包装在类似于汇编模样的外衣下我看依稀可见class, System.Object, method, public, string这些面向对象高级语言中的熟悉面孔不同的只是多了很多benforefieldinit参考[你必须知道的.NET]第二十三回品味细节深入.NET的类型构造器, ret, maxstack, ldstr, stloc这些陌生的指令。然而IL并非一个怪胎而正是基于其本身面向对象的汇编式风格才造就了IL代码成为名副其实的“中间语言”的重任。通过IL代码CLR就可在编译时由JIT编译转换为Native Code我们将在下节继续分析这个过程的来龙去脉。
欲知后事如何且听下文继续:-) 元数据是什么IL是什么元数据和IL在JIT编译时