CLR via C#-----CLR执行模型

1. 将源代码编译成托管模块

事实上,可将编译器视为语法检查器和“正确代码”分析器,它们检查代码,确定你所写的一起都有意义,并输出对你的意图进行描述的代码,编译器的结果都是托管模块(标准的32位Microsoft Windows可移植执行体(PE32)文件或者标准的64位Windows可移植执行体(PE32+)文件)

<托管模块的各个部分>

组成部分说明
PE32或PE32+头标准Window PE文件头,类似于“公共对象文件格式”头。如果这个头使用PE32格式,文件能在Windows的32或64位版本上运行,如果这个头使用PE32+格式,文件只能在Windows的64位版本上运行。这个头还标识了文件类型,包括GUI,CUI,或者DLL,并包含一个时间标记来支出文件的生成时间。对于只包含IL代码的模块,PE32+头的大多数信息会被忽视。如果只包含本机(native)CPU的代码的模块,这个头包含CPU代码有关的信息
CLR头包含使这个模块成为托管模块的信息(可由CLR和一些实用程序解释)。头中包含要求的CLR版本,一些标志(flag),托管模块入口方法(Main方法)的MethodDef元数据token已经模块的元数据,资源,强名称,一些标志及其他不太重要的数据项的位置大小
元数据每个托管模块都包含元数据表。主要有两种表:一种表描述源代码中定义的类型和成员,另一种描述源代码引用的类型和成员
IL(中间语言)代码编译器编译源代码时生成的代码。在运行时,CLR将IL编译成本机CPU指令

由于编译器同时生成元数据和代码,吧他们绑定在一起,并嵌入最终生成的托管模块,所以元数据和它描述的IL代码永远不会失去同步

元数据有多种用途,下面列举部分

  • 元数据避免了编译时对原生C/C++头和库文件的需求,因为在实现类型/成员的IL代码文件中,已包含有关引用类型/成员的全部信息。编译器直接从托管模块读取元数据
  • MIcrosoft Visual Studio用元数据帮助你写代码。“智能感知(IntelliSense)”技术会解析元数据,告诉你一个类型提供了哪些方法,属性,事件和字段,对于方法,还能告诉你需要的参数
  • CLR的代码验证过程使用元数据确保代码只会执行“类型安全”的操作
  • 元数据允许将对象的字段序列化到内存块,将其发送给另一台电脑,然后反序列化,在远程机器上重建对象状态
  • 元数据允许垃圾回收器跟踪对象生存周期。垃圾回收器能判断任何对象的类型,并从元数据知道那个对象中的哪些字段引用了其他对象

2. 将托管模块合并成程序集

程序集是抽象概念,是一个或多个模块/资源的逻辑性分组,也是重用,安全性以及版本控制的最小单元。利用程序集,一组文件可作为一个单独的实体对待。可以在不同的地方部署文件,同时仍然将所有文件作为一个整体对待

<将托管模块合并成程序集>
将托管模块合并成程序集

3. 加载公共语言运行时

要知道是否已安装.NET Framework,只需检查C:\Windows\System32目录中的MSCorEE.dll,存在则表明已安装

C#编译器提供了一个/platform命令行开关选项,这个选项允许指定最终生成的程序集在某个平台允许,如果不指定具体平台的话。默认选项就是anycpu
Visual studio用户要想设置目标平台,可以打开项目的属性页,从“生成”选项卡的“目标平台”列表中选择一个选项
在这里插入图片描述

</platform开关选项对生成的模块的影响以及在运行时的影响>

/platform开关生成的托管模块x86 Windowsx64 WindowsARM Windows RT
anycpu(默认)PE32/任意CPU架构作为32位应用程序运行作为64位应用程序运行作为32位应用程序运行
anycpu32bitpreferredPE32/任意CPU架构作为32位应用程序运行作为WoW64应用程序运行作为32位应用程序运行
x86PE32/x86作为32位应用程序运行作为WoW64应用程序运行不运行
x64PE32+/x64不运行作为64为应用程序运行不运行
ARMPE32/ARM不运行不运行作为32位应用程序运行

Windows检查EXE文件头,决定是创建32位还是64位进程后,会在进程地址空间加载MSCorEE.dll的x86,x64或ARM版本

4.执行程序集的代码

高级语言通常只公开CLR全部功能的一个子集,IL汇编语言允许开发人员访问CLR的全部功能

<方法的首次调用>
方法的第一次调用

在Windows不同版本中运行将会得到不同指令(x86,x64,ARM)

<方法的第二次调用>
方法的第二次调用

由于已对WriteLine的代码进行了验证和编译,所以会直接执行代码块中的代码,完全跳过JITCompiler函数,方法仅在首次调用时才会有一些性能损失

CLR的JIT编译器会对本机代码进行优化,两个C#编译器开关会影响代码优化

编译器开关设置C# IL代码质量JIT本机代码质量
/optimize-/debug-(默认)未优化有优化
/optimize-/debug(+/full/pdbonly)未优化未优化
/optimize+/debug(-/+/full/pdbonly)有优化有优化

使用/optimize-,在C#编译器生成的未优化IL代码中,将包含许多NOP(no-operation,空操作)指令,还包含许多跳转到下一行代码的分支指令,Visual studio利用这些指令在调试期间提供“编辑并继续(edit-and-continue)”功能

托管代码相较于非托管代码的优势

  • JIT编译器能判断应用程序是否运行在Intel Pentium 4 CPU上,并生成相应的本机代码来利用Pentium 4支持的任何特殊指令。相反非托管应用程序通常是针对具有最小功能集合的CPU编译的,不会使用能提升性能的特殊指令
  • JIT编译器能判断一个特定的测试在它运行的机器上是否总是失败。例如,假定一个方法包含以下代码
    if(nunberOfCPU>1){
    ...........
    }
    如果主机只有一个CPU,JIT编译器不会为上述代码生成任何CPU指令。在这种情况下本机代码将针对主机进行优化,最终代码会变得更小,执行的更快

IL基于栈。这意味者它的所有指令都要将操作数压入(push)一个执行栈,并从栈弹出(pop)结果。将IL编译成本机CPU指令时,CLR执行一个名为验证(verification)的过程。这个过程会检查高级IL代码,确定代码所做的一切都是安全的。托管模块的元数据包含验证过程要用到的所有方法及类型信息。
Microsoft C#编译器也允许开发人员写不安全的(unsafe)代码。不安全的代码允许直接操作内存地址,并可操作这些地址处的字节。C#编译器要求包含不安全代码的所有方法都用unsafe关键字标记,除此之外,C#编译器要求使用/unsafe编译器开关来编译源代码。Microsoft提供一个名为PEVerify.exe的实用程序,它检查一个程序的所有方法,并报告其中含有不安全代码的方法。

5.本机代码生成器:NGen.exe

使用.Net Framework提供的NGen.exe工具,可以在应用程序安装到用户的计算机上时,将IL代码编译成本机代码。
NGen.exe编译的优缺点
优点

  • 提高应用程序的启动速度
  • 减小应用程序的工作集

缺点

  • 没有知识产权保护
  • NGen生成的文件可能失去同步
  • 较差的执行时性能

6.Framework类库

.Net Framework包含Framework类库(Framework Class Library,FCL)是组DLL程序集的统称,其中包含数千个类型定义,每个类型都公开一些功能。
部分应用程序

  • Web服务(Web service)
  • 基于HTML的Web窗体/MVC应用程序(网站)
  • “富”Windows GUI应用程序
  • Windows控制台应用程序
  • Windows服务
  • 数据库存储过程
  • 组件库

许多类型都允许自定义其行为,你只需从所需的FCL类型派生出自己的类型再进行自定义即可。

7.通用类型系统

通用类型系统(Common Type System,CTS),CTS规范规定,一个类型可以包含零个或多个成员。

  • 字段(Field)
    作为对象状态一部分的数据变量。字段根据名称和类型来区分
  • 方法(Method)
    针对对象执行操作的函数,通常会改变对象状态
  • 属性(Property)
    属性运行在访问值之前校验输入参数和对象状态
  • 事件(Event)
    事件在对象以及其他相关对象之间实现通知机制

CTS还指定可见性规则和类型成员的访问规则

  • private
    成员只能由同一个类(Class)类型中的其他成员访问
  • family
    成员可由派生类访问,不管是否在同一个程序集中。许多语言(比如C++和C#)都用protected修饰符标识family
  • family and assembly
    成员可由派生类访问,但这些派生类必须在同一个程序集中定义。许多语言(比如C++和C#)都没有提供这个访问控制,但IL汇编语言不在此列
  • assembly
    成员可由同一个程序集中的任何代码访问。许多语言都用internal修饰符来标识assembly
  • family or assembly
    成员可由任何程序集中的派生类访问。成员也可由同一个程序集中的任何类型访问。C#用protected internal修饰符标识family or assembly
  • public
    成员可由任何程序集中的任何代码访问

CTS规定所有类型最终必须从预定义的System.Object类型继承。System.Object类型允许

  • 比较两个实例的相等性
  • 获取实例的哈希代码
  • 查询一个实例的真正类型
  • 执行实例到的浅(按位)拷贝
  • 获取实例对象当前状态的字符串表示

8.公共语言规范

Microsoft定义了“公共语言规范”(Comman Language Specification,CLS),他详细定义了一个最小功能集。任何编译器只有支持这个功能集,生成的类型才能兼容由其他符合CLS,面向CLR的语言生成的组件。

<CLR,CTS,CLS相对关系>
CLR,CTS,CLS相对关系

9.与非托管代码的互操作性

CLR支持三种互操作情形

  • 托管代码能调用DLL中的非托管函数
    托管代码通过P/Invoke(Platform Invoke)机制调用DLL中的函数
  • 托管代码可以使用现有COM组件(服务器)
    可参考 .NET Framework SDK提供的TlbImp.exe工具
  • 非托管代码可以使用托管类型(服务器)
    可参考 .NET Framework SDK提供的TlbExp.exe和RegAsm.exe工具
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值