说在,开篇之前

很早就有说说Metadata(元数据)和IL(中间语言)的想法了,一直在这篇开始才算脚踏实地的对这两个阶级兄弟投去些细关怀,虽然来得没有《第一回:恩怨情仇:is和as》那么迅速,但是Metadata和IL却是绝对重量级的内容,值得我们在任何时间关注,本文就是开始。

1 引言

你可曾想到,我们的C#代码,编译之后究竟为何物?你可曾认知,我们的可执行程序,运行之时的轨迹究竟为哪般?那么,本文通过对Metadata(元数据)和IL(Intermediate Language, 中间语言)的认识开始,来逐步给出答案。在这个探索轨迹上,元数据、IL、程序集、程序域、JIT、虚分派、方法表和托管堆这些形形色色的神秘嘉宾将在某个时刻不期而遇,作为你必须知道的.NET 系列2.0版本的一部分,本文首先从认识元数据和IL这两位重量级选手开始,而其他的嘉宾也将很快登场。

2 初次接触

在事实上,编译之后的cs代码被组织为两种基本的元素:元数据(Metadata)和IL。我们可以以最简单的方式来了解程序集(*.dll)或可执行文件(*.exe)中包含的Metadata和IL的秘密,这种方式就是我们常说的反编译,打开ILDasm并加载实现准备的程序集,我们可以看到托管PE文件的相关内容:

详细的结构信息和IL代码分析,可以参见[你必须知道的.NET]第3章 “一切从IL开始”的介绍,在此就不做太多的分析。另外,我们可以通过执行“View/MetaInfo/Show!”或者Ctrl+M快捷键来获取该程序集所使用的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节 “从Hello,world开始认识IL”对其有过详细的描述,此不赘述。

PE文件头,标准Windows PE头文件(PE32或PE32+),PE文件的基本信息,例如文件类型,创建时间,本地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(上) <第三篇>的更多相关文章

  1. java面试必备知识点-上中下三篇写的很详细

    参考博客:写的还是相当的经典 http://www.cnblogs.com/absfree/p/5568849.html 上中下三篇写的很详细 http://blog.csdn.net/riverfl ...

  2. 认识元数据和IL(中)<第四篇>

    书接上回[第二十四回:认识元数据和IL(上)],我们对PE文件.程序集.托管模块,这些概念与元数据.IL的关系进行了必要的铺垫,同时顺便熟悉了以ILDASM工具进行反编译的基本方法认知,下面是时候来了 ...

  3. 认识元数据和IL(下)<第五篇>

    书接上回: 第二十四回:认识元数据和IL(上) , 第二十五回:认识元数据和IL(中) 我们继续. 终于到了,说说元数据和IL在JIT编译时的角色了,虽然两个回合的铺垫未免铺张,但是却丝毫不为过,因为 ...

  4. [你必须知道的.NET]第二十四回:认识元数据和IL(上)

    发布日期:2009.02.24 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 很早就有说说Metadata(元数据)和IL(中 ...

  5. [你必须知道的.NET]第二十五回:认识元数据和IL(中)

    发布日期:2009.02.25 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 书接上回[第二十四回:认识元数据和IL(上)], ...

  6. 第三篇 Integration Services:增量加载-Adding Rows

    本篇文章是Integration Services系列的第三篇,详细内容请参考原文. 增量加载是什么增量加载仅加载与先前加载差异的.差异包括:->新增的行->更新的行->删除的行通过 ...

  7. [老老实实学WCF] 第三篇 在IIS中寄存服务

    老老实实学WCF 第三篇 在IIS中寄宿服务 通过前两篇的学习,我们了解了如何搭建一个最简单的WCF通信模型,包括定义和实现服务协定.配置服务.寄宿服务.通过添加服务引用的方式配置客户端并访问服务.我 ...

  8. (转) [老老实实学WCF] 第三篇 在IIS中寄存服务

    第三篇 在IIS中寄宿服务 通过前两篇的学习,我们了解了如何搭建一个最简单的WCF通信模型,包括定义和实现服务协定.配置服务.寄宿服务.通过添加服务引用的方式配置客户端并访问服务.我们对WCF的编程生 ...

  9. 第三篇——第二部分——第六文 监控SQL Server镜像

    原文:第三篇--第二部分--第六文 监控SQL Server镜像 原文出处:http://blog.csdn.net/dba_huangzj/article/details/26846203 要优化, ...

随机推荐

  1. 《Programming WPF》翻译 第9章 3.自定义功能

    原文:<Programming WPF>翻译 第9章 3.自定义功能 一旦你挑选好一个基类,你将要为你的控件设计一个API.大部分WPF元素提供属性暴露了多数功能,事件,命令,因为他们从框 ...

  2. curl 网页抓取

    如果要把这个网页保存下来,可以使用-o参数,这就相当于使用wget命令了. curl -o [文件名] www.tvbs.cc 二.自动跳转 有的网址是自动跳转的.使用-L参数,curl就会跳转到新的 ...

  3. IDX爱定客 | 氪加

    IDX爱定客 | 氪加 个性化定制鞋网站,在线定制只需三分钟

  4. 【转】网络视频监控P2P解决方案

    一.摘要 本文分析了日益增长的民用级别家庭和个人网络视频监控市场的需求特点,并给出了一种经济可行易于大规模部署的P2P解决方案. 由于篇幅有限,本文只给出了方案的思路,未对更深入的技术细节做详细的论述 ...

  5. SAX解析和生成XML文档

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本人声明.否则将追究法律责任. 作者: 永恒の_☆ 地址: http://blog.csdn.net/chenghui031 ...

  6. linux修改系统时间

    当你把linux还原到某个点的时候,vmware帮不了你把系统时间也给重设了.所以这时候就要手工来搞.关于咋设linux时间.网上介绍也很多,但是都是抄来抄去的东西.那怎么才能高效快捷的设置系统时间呢 ...

  7. 线程:Exchanger同步工具

    可以在对中对元素进行配对和交换的线程的同步点,类似于交易,A拿着钱到达指定地点,B拿着物品到达指定地点,相互交换,然后各自忙各自的事去了. package ch03; import java.util ...

  8. 前端--关于javascript基础

    首先javascript不是浏览器的附属品,只能说它大多数的运行环境是在浏览器中的,但又不仅仅局限于浏览器中.它是一门真正的程序设计语言,在这方面它和java.c.c++.c#是等同的,只不过它不直接 ...

  9. IE iframe 中 js 的 cookie 读写不到的解决办法

    1.看这里(改服务器配置) http://www.cr173.com/html/16696_1.html 2.使用object模拟iframe,不使用iframe框架 <html> < ...

  10. LA 3662 Another Minimum Spanning Tree (曼哈顿距离最小生成树 模板)

    题目大意: 曼哈顿最小距离生成树 算法讨论: 同上. 这回的模板真的准了. #include <iostream> #include <cstring> #include &l ...