《CLR via C#》读书笔记(2) -- .NET程序的运行模型(上)

  •          平台确定

        通过第一节,我们知道:编译器在PE文件中写入了大量的元数据。其中在PE32(+)中有一个machine字段用来标识该程序集所面向的平台(一般应该是x86,x64,不确定(Any CPU)).
        在当前运行平台与程序集面向平台兼容的情况下,程序集为以所指定的平台类型运行(比如:面向x86则分配32从位地址空间,为其加载x86版本的所引用系统程序集;面向x64则为其分配64位的地址空间,为其加载x64版本的所引用系统程序集;如果any cpu则根据当前运行平台确定。)

 

  •         实例解剖CLR运行模型
    以如下代码为例:
    class Program
    {
          static  void Main( string[] args)
         {
                Console.WriteLine( " Hello World ");
         }
    }

    生成如下IL代码:
    .method  /* 06000001 */  private hidebysig  static 
             void  Main( string[] args) cil managed
    //  SIG: 00 01 01 1D 0E
    {
      .entrypoint
       //  Method begins at RVA 0x2050
      
    //  Code size       13 (0xd)
      .maxstack   8
      IL_0000:   /*  00   |                   */ nop
      IL_0001:   /*  72   | (70)000001        */ ldstr       " Hello World "  /*  70000001  */
      IL_0006:   /*  28   | (0A)000011        */ call        void [mscorlib /* 23000001 */]System.Console /* 01000013 */::WriteLine( string/*  0A000011  */
      IL_000b:   /*  00   |                   */ nop
      IL_000c:   /*  2A   |                   */ ret
    //  end of method Program::Main
    生成如下CLR Header(部分):
    ----- CLR Header:
    Header size:                        0x00000048
    Major runtime version:              0x0002
    Minor runtime version:              0x0005
    0x00002068 [ 0x00000640] address [size] of Metadata Directory:       
    Flags:                              0x00000003
    Entry point token:                  0x06000001
    CLR将这个程序Run起来的步骤如下:

    1. 找到入口方法
    我们可以看到通过CLR Header的Entry point token字段可以知道,该程序集的入口方法是位于MethodDef表中(token 0x06 代表是methoddef表)的第一条记录所代表的方法。通过查询MethodDef表我们可以找到该方法的IL在该文件中的偏移。CLR会将IL编译为本地CPU指令后再执行该代码。

    2. 查找引用类型/成员
    其实该代码非常简单,只是调用System.Console.WriteLine方法向输出流上写了一段字符“Hello World”.
    从IL指令可以看到,该行代码分为两部分来执行:第一步是将"Hello World"字符串入栈;第二步是调用System.Console类型的WriteLine方法
    第一步没什么可说的,主角是第二步。
    第二步是调用一个外部引用的类型System.Console的方法WriteLine. CLR对该方法的调用过程其实也是费很大一番功夫,主要包括如下几步:
    (1) 加载程序集
          这一步其实是在CLR JIT将Main方法编译成本地代码时就进行了的。CLR会查找Main方法中所有的引用的外部类型,并将这些外部类型所在的程序集全部加载进来。
    (2) 查找方法IL
         对于引用的外部类型方法,CLR查找IL代码步骤如下:
         -首先查找MemberRef表,找出定义该方法的类型
         -然后查找TypeRef表,找出该类型是在当前程序集还是在别的程序集
         --若是当前程序集,则通过ModuleRef能找到其所在Module,通过Module的MethodDef元数据可以知道方法的IL的存放位置偏移
         --若不是当前程序集,则通过查找目标程序集的清单元数据查找到目标Module,最后也是通过MethodDef元数据表找到IL的存放位置。

    对于当前的例子,CLR首先从MemberRef中找到WriteLine方法对应的条目,然后找到其所在类型System.Console在TypeRef中所对应的条目。
    通过该元数据可以知道其属于mscorlib这个程序集,并可以导航至AssemblyRef元数据表。通过AssemblyRef元数据表,我们便可以定位到mscorlib这个程序集
    的清单文件,进行最终找到定义System.Console这个类型的Module,从该Module的MethodDef数据表中找到WriteLine方法的IL存放地址。
    (3) 构造数据结构保存方法的本地指令地址
         因为此处访问了System.Console这个类型的方法。CLR会
         首先在内存中为Console这个类型创建一个数据结构(假设称之为ConsoleLinker),这个数据结构是为了保存Console中所有方法的本地代码地址。但是当这个数据结构被初始化的时候,每一个方法的本地代码地址都被设为JIT的JITCompiler方法的地址。
         然后,当调用WriteLine方法时JITCompiler函数会被调用,这个函数所做的事情是先将步骤(2)所找到的方法IL代码编译成本地代码,然后保存于内存中,并将
    ConsoleLinker中保存WriteLine方法地址的地方设为本地代码的地址。(当以后再次访问这个方法时,便可以直接执行本地代码,而不用经过JIT编译了)

    (4) 执行方法调用
    通过以上步骤,已经可以知道所引用外部方法的本地代码地址.CLR会直接去执行该地址所保存的本地代码。

    最后附上两幅图,我觉得最能代表整个执行过程:
    第一幅,CLR查找所引用的类型的模型:

    第二幅,当调用引用的类型方法时,其调用模型为:

转载于:https://www.cnblogs.com/Code-life/archive/2012/11/20/2779891.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值