zoukankan      html  css  js  c++  java
  • CLR

    前言

      好记性不如烂“笔头”系列。。。

      目录

    托管模块

      面向 CLR 的编译器在编译源文件时最终会编译成一个 PE(可移植执行体 , Portable Executable) 文件 .

      而一个托管模块的组成由下面几个部分组成:

    组成部分 说明
    PE32 或 PE32+ 头 标准WINDOW PE 文件头,类似于 "公共对象文件格式"(Common Object File Format, COFF) 头。如果这个头使用 PE32 格式,则文件能在Windows 32 位或 64 位版本上运行。如果使用 PE32+ 格式,则只能在Windows 64 位版本上运行。这个头还包括其它信息。
    CLR 头 包含使这个模块成为一个托管模块的信息(可由CLR 和一些实用程序解释)。头中包含了需要的CLR 版本,一些标志(Flag),托管模块入口方法(Main 方法)的MethodDef 元数据标记(token),以及模块的元数据、资源、强名称、一些Flag以及其它不太重要的数据项的位置大小。
    元数据 当局者迷how func;asdf asdfksdfasd fsadf

     每个托管模块都包含元数据表。主要有两种类型的表:一种类型的表描述源代码中定义的类型与成员;另一种类型的表描述源代码中引用的类型与成员。

    一般为描述其他对象而建立的对象都会加上“元”,元数据就是描述其他数据的数据。在.NET 中,其他数据指.NET 对象,引用到的对象及他们的关系。

    IL (中间语言) 代码 编译器编译时生成的代码。在运行时,CLR 将IL 编译成本地CPU 指令。(IL 代码有时称为托管代码)

    JIT(just-in-time)

      执行一个方法时,必须将将IL 由JIT (just-in-time ,即时编译器)转换成本地CPU 指令。

      分析书中的例子:

    just-in-time

      在Main方法执行之前,CLR会检测出Main的代码引用的所有类型。这将导致CLR分配一个内部数据结构,它用来管理对所用引用的类型的访问。 例如上图,Main方法引用了一Console类型,这导致CLR分配一个内部结构。在这个内部结构中,Console类型定义的每个方法都有一个对应的 记录项。每个记录项都容纳一个地址,根据此地址既可以找到方法的实现。对这个结构进行初始时,CLR将每个记录项都设置成(指向)包含在CLR内部的一个 未文档化的函数。我将这个函数成为JITCompiler。

      Main首次调用WriteLine时,JITCompiler函数会被调用。JITCompiler函数负责将一个方法IL代码编译成本地 CPU指令。由于IL是“即时”(just in time)编译的,所以通常CLR的这个组件称为JITter或者JIT编译器。

      JITCompiler函数被调用时,它知道要调用的是哪个方法,以及具体是什么类型定义了该方法。然后,JITCompiler会在定义程序集的元数据中查找被调用的方法的IL。接着,JITCompiler验证IL代码,并将IL代码编译成本地CPU指令。本地CPU指令被保存到一个动态分配的内存块中。然后,JITCompiler返回CLR为类型创建的内部数据结构,找到与被调用的方法对象的那一条记录,修改最初对 JITCompiler的引用,让它现在指向内存块中的地址。最后,JITCompiler函数跳转到内存块中的代码。

      第二次调用WriteLine。这一次,由于对WriteLine的代码进行了验证和编译,所以直接执行内存块中的代码,完全跳过JITCompiler函数。

      第二次调用WriteLine的情况 :

    just-in-time-2

    元数据

      元数据是一个二进制数据块,由几个表构成。这些表分为三个类别:定义表(definiton talbe)、引用表(reference table)和清单表(mainfest table)。

      常用的元数据定义表:

    元数据定义表名称  说明
     ModuleDef  总是包含一个用于标示模块的记录项
     TypeDef  模块中定义的每个类型都在这个定义表中有一个对应的记录项。
     MethodDef  模块中定义的每个方法都在这个定义表中有一个对应的记录项。
     FieldDef  模块中定义的每个字段都在这个定义表中有一个对应的记录项
     ParamDef  模块中定义的每个参数都在这个定义表中有一个对应的记录项
     PropertyDef  模块中定义的每个属性都在这个定义表中有一个对应的记录项
     EventDef  模块中定义的每个事件都在这个定义表中有一个对应的记录项

       

        使用ILDasm.exe 查看TypeDef 元数据定义表:

    TypeDef

      常用的引用元数据表:

     引用元数据表名称  说明
     AssemblyRef  模块中引用的每个程序集在这个表中都有一个对应的记录项
     ModuleRef  模块引用的每个类型可能是由别的PE模块实现的,所有那些模块在这个表都有一个记录项
     TypeRef  模块引用的每个类型在这个表中都有一个对应的记录项
     MemberRef  模块引用的每个成员都在这个表中有一个对应的记录项

        使用ILDasm.exe 查看AssemblyRef 元数据引用表:

    AssemblyRef

        使用ILDasm.exe 查看TypeRef 元数据引用表:

    TypeRef

      清单元数据表:

    清单元数据表名称 说明
     AssemblyDef  如果该模块标示的是一个程序集,就在这个元数据表中包含单个记录项。该记录项列出了程序集名称(不含路径和扩展名)、版本(major,minor,build和revision)、语言文化(culture)、一些标志(flag)、哈希算法以及发布者的公钥。
     FileDef  作为程序集一部分的每个PE文件和资源文件在这个表中都有一个对应的记录项。
     MainifestResourceDef  作为程序集一部分的每个资源在这个表中都有一个对应的记录项
     ExportedTypesDef  从程序集的所有PE模块中导出的每个public类型中在这个表中都有一个对应的记录项。

        使用ILDasm.exe 查看AssemblyDef 清单元数据表

    AssemblyDef

    CLR 解析类型引用

    Hello World

        编译这段代码并生成一个程序集,假定为Program.exe 。运行这个应用程序时,CLR 执行步骤为:

            1:会加载并初始化它。

            2:读取程序集的CLR 头,查找标识了应用程序入口的方法(Main)的MethodDefToken 。

            3:检索MethodDef 元数据表,找到该方法的IL 代码在文件中的偏移量,把这些代码JIT(Just In Time)编译成本地(Native)代码。

        当以下的IL 代码进行JIT 编译时,CLR 会检查参类型和成员的所有引用,并加载定义了它们的程序集(如果尚未加载)。

    Hello World

    Hello World

        类型绑定过程如下:

    Hello World

        

  • 相关阅读:
    Dynamically allocated memory 动态分配内存【malloc】Memory leaks 内存泄漏
    const pointers
    heap是堆,stack是栈
    Java中使用Cookie
    Postman注册、登录、导出、导入
    HttpServletRequest get post 入参
    判断设置的时间是否大于当前时间
    JS回车登录
    一个普通的Ajax
    Java工具类
  • 原文地址:https://www.cnblogs.com/yanshicao/p/3721280.html
Copyright © 2011-2022 走看看