CLR笔记:1.CLR的执行模型

术语:    CLR :Common Language Runtime 公共语言运行期,有多种不同编程语言使用的运行库
                托管模块:Managed Module,一个标准的MS Window可移植执行体文件(32位PE32或64位PE32+)
                IL:Intermediate Language 中间语言,又叫托管代码(由CLR管理它的执行)
                元数据:metadata,一系列特殊的数据表
                程序集:Assembly,抽象的
                JIT:just-in-time 即时编译,将IL编译成本地CPU指令(本地代码)
                FCL:Framework Class Library,Framework 类库
                CTS:Common Type System,通用类型系统,描述了类型的定义及其行为方式
                CLI:Common Language Infrastructure,公共语言基础结构,这是MS提交给ECMA的一个标准,由CTS和其他Framwork组件构成
                CLS:Common Language Specfication,公共语言规范,详细规定了一个最小特性集

1.1    将源代码编译成托管模块
CLR编译过程: C#源码文件——C#编译器编译——托管模块(IL和元数据)

托管模块的各个部分:
       1.PE32或PE32+头 
            标志了文件类型,GUI/CUI/DLL,文件生成时间,在32位还是64位上运行
       2.CLR头
            CLR版本,入口方法,模块元数据,资源,强名称
       3.元数据
            3种类型的表
       4.IL代码

元数据包括:
       1.描述了模块中定义的内容,比如类及其成员
       2.指出了托管模块引用的内容,比如导入的类及其成员
       3.清单manifest,描述了构成Assembly的文件,由Assembly中的文件实现的公共导出类型,与Assembly相关联的资源/数据文件
元数据总是嵌入到与代码相同的EXE/DLL中,始终与IL保持同步。

元数据用途:
       1.消除了对头/库文件的依赖,直接从托管模块中读取
       2.智能感知,从元数据中解析
       3.代码验证,使用元数据确保代码只执行安全操作
       4.正反序列化
       5.垃圾收集器跟踪对象的生存期以及对象的类型

1.2    将托管模块合并成程序集
程序集:一个或多个托管模块/资源文件的逻辑分组,是最小的重用,安全性以及版本控制单元。
        既可以生成单文件程序集,也可以生成多文件程序集,这由编译器工具决定。
        CLR是和程序集一起工作的,而不是和托管模块

1.3    加载CLR
    CLRVer命令,查看机器上所有CLR版本
    csc的 /plattform开关,决定生成什么样的程序集:AnyCPU,x86,x64,Itanium

1.4    执行Assembly代码
    ILAsm命令,将IL编译成Assembly;ILDasm将Assembly编译成IL。
    高级语言(C#)只是CLR的一个子集,IL则允许访问CLR的所有功能。
    JITCompiler函数,又名JIT编译器(JITter)
        在方法首次执行时,CLR检测出Main的代码引用的所有类型,于是CLR分配一个内部数据结构,用于管理对引用类型的访问。
        在这个内部结构中,每个方法都有一条对应的纪录以及地址。
        对此结构进行初始化时,CLR将每条纪录都设置为CLR内部包含的一个未文档化的函数,即 JITCompiler函数。
        JITCompiler函数被调用时,找到相应方法的IL,编译成本地CPU指令,并保存到一个动态内存块中,将该内存地址存入内部结构中,最后JITCompiler函数会跳转到内存块中的代码,执行。
        第二次执行该方法时,不需要再编译,直接执行内存块中的代码。

        JIT将本地代码保存在动态内存中,一旦程序终止,本地代码会被丢弃。

        csc命令有2个开关会影响代码的优化:/optimize ,/debug

开关设置 IL代码质量 JIT本地代码质量
/optimize- ,/debug- 未优化 优化 默认设置
/optimize- ,/debug(+/full/pdbonly) 未优化 未优化 VS2005 Degug状态
/optimize+ ,/debug(-/full/pdbonly) 优化 优化 VS2005 Release状态

生成未优化的IL时,会在IL中生成NOP指令用于调试,设置断点。

IL是基于堆栈的。所有指令都是:将操作数压栈,结果则从栈中弹出
IL有安全验证机制,保证每一行IL代码是正确的,不会非法访问内存,每个托管EXE都在独自的AppDomain中运行。

不安全代码:允许C#直接操作内存字节,在COM互操作时使用,csc以/unsafe开关标记包含不安全代码,其中所有方法都使用unsafe关键字。
PEVerify命令检查程序集所有方法,指出其中的不安全代码方法。

1.5    本地代码生成器 NGEN.exe
NGEN.exe将IL预先编译到硬盘文件中,可以加快程序的启动速度,减小程序的工作集(所有加载该程序集的AppDomain不再copy其副本,因为该程序集已经与编译到文件中,是代码共享的)。
缺点是:
    不能保护IL外泄
    生成的文件可能失去同步
    因为在文件中要计算首选基地址,而NGEN是静态计算好的,所以要修改基地址,速度会慢下来
    较差的执行性能,NGEN生成的代码没有JIT好。
如果不能使用NGEN生成的文件,会自动加载JITCompiler。

1.7    CTS
    CTS的一些规定:
        1.一个类型可以包含0个或多个成员
        2.类型可视化以及类型成员的访问规则
        3.定义了继承,虚方法,对象生成期的管理规则
        4.所有类型最终都从预定义的System.Object继承

1.8    CLS
    如果在C#中定义的类型及其方法,可以在VB中使用,那么,就不能在C#中定义CLS外的任何public/protected特性,privated的类型及其成员不受限制。
    C#可以有仅大小写不同的两个方法——不符合CLS,所以不能是public的。
    
    使用[assembly:CLSComplant(true)]标志程序集,告诉编译器检查该程序集的CLS相容性。书上写得不明白,我这里做了一个测试:

using System;

[assembly: CLSCompliant(
true)]

namespace ClassLibrary2
{
    
public class Class1
    
{

        
public void A()
        
{

        }


        
public void a()
        
{

        }

    }

}

注意,[assembly:CLSComplant(true)]要写在namespace外。
我定义了两个不同方法A和a,编译器会有警告,说这样的语法不兼容CLS;如果去掉[assembly:CLSComplant(true)]声明,那么不会有这个警告;如果将a方法改为private,则不会有警告。
中途我使用了ILDasm观察这个dll,发现两个方法A和a都存在于IL中,说明IL的语法范围也大于CLS。
在VB中,我添加了对此dll的引用:

Imports ClassLibrary2

Module Module1

    
Public Class T
        
Public Function A() As Integer

            
Dim c1 As Class1 = New Class1()


        
End Function

    
End Class


End Module

发现,在c1.后面不会有A或a方法的智能感知,说明VB不能识别不符合CLS的语法。如果修改了dll中的a方法为private或者删除a方法,则在VB中可以智能感知到A方法。
可以得出结论,不符合CLS的语法,在另一种语言中是看不到的。
    
1.9 COM互操作
    3种互操作情形:
        1.托管代码可以调用DLL中包含的非托管函数,如Kernal32.dll,User32.dll
        2.托管代码可以使用现成的COM组件
        3.非托管代码可以使用托管类型(C#写的ActiveX控件或shell扩展)

原文地址:https://www.cnblogs.com/Jax/p/893591.html