Chapter1 CLR的执行模式

1.1将源代码编译成托管模块

一、托管模块的各个组成部分

1.PE32或PE32+头,标准的WindowsPE文件头,如果这个头使用的是PE32格式,文件只能在32或64位系统上运行,如果头是PE32+格式只能在64位系统上运行
2.CLR头
3.元数据,一组数据表,主要包含两种表:①描述源代码中定义的类型和成员②描述源代码引用的类型和成员。
4.IL(中间语言)代码,编译器编译源代码时生成的代码,在运行时,CLR将IL编译成本地CPU指令。

二、元数据多种用途

1.编译时,元数据消除了对本地C/C++头和库文件的需求(在IL代码文件中,已经包含和引用的类型/成员有关的全部信息,编译器可以直接从托管模块读取元数据)。
2.使用元数据帮你写代码————智能感知。
3.CLR的代码验证过程使用元数据确保代码只执行“类型安全”的操作。
4.序列化-反序列化。
5.允许GC跟踪对象的生存期。

1.2将托管模块合并成程序集

程序集:一个或多个模块/资源文件的逻辑性分组,其次,是重用、安全性以及版本控制的最小单元。它可以是一个可执行应用程序、也可以是个DLL

1.3加载公共语言运行时

CLR管理程序集代码的执行,所以必须安装.Net Framework
要知道是否安装.Net Framework,只需检查%SystemRoot%\System32目录中的MSCorEE.dll文件是否存在。
.Net Framework SDK提供了一个名为CLRVer.exe的命令执行程序,它能列出已经安装了的所有CLR版本
/platform开关选项对生成的模块的影响以及在运行时的影响

1.4执行程序集的代码

托管程序集同时包含元数据和IL,IL是与CPU无关的机器语言,但比大多数CPU机器语言要高级,还可以创建初始化对象、调用对象上的虚方法以及直接操作数组元素、捕获异常并抛出。综上所述可将IL视为一种面向对象的机器语言,IL可有ILAsm.exe的IL汇编器和ILDasm.exe的IL反汇编。 为了执行一个方法,首先必须把它的IL转换成本地CPU指令,这是CLR的JIT编译器的职责。
一个方法首次调用时发生的事情

第二次调用时发生的事情

Main函数第二次调用WriteLine时由于已对WriteLine的代码进行了验证和编译,所以会直接执行内存块中的代码,完全跳过JITCompiler函数(一旦进程终止,编译好的代码将被丢弃)。
还要注意的是,CLR的JIT会对本地代码进行优化,可能会花费一些时间,但性能更出色
有两个C#编译器的开关会影响代码的优化:/optimize和/debug

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

使用/optimize-在C#编译器生成的未优化的IL代码中,将包含许多NOP(no-operation,空操作)指令,如果在调试期间,还可以在控制流程(如for,while,do,if,else,try,catch和finally语句块)上设置断点,使代码更易于调试,相反如果优化IL代码,则不能在上述流程中调试。 另外只有在指定/debug(+/full/pdbonly)开关的前提下编译器才会生成Program Database(PDB)文件
在Visual Studio中项目的
Debug(调试)配置对应的是/optimize-和/debug:full开关。
Release(发布)配置对应的是/optimize+和/debug:pdbonly开关

1.4.1 IL和验证

IL是基于栈的,在编译IL到本地指令的过程中,CLR会有一个验证的过程(包括方法正确数量的参数,正确的参数类型,返回值正确的使用,都有返回语句),在托管模块的元数据中包含了所有方法和类型信息。

1.4.2 不安全的代码

不安全代码允许直接操作内存地址,并可操作这些地址处的字节(通常在与非托管代码进行交互时,或提高对效率极高的算法时才需要这样做)
C#采用unsafe关键字,要求使用/unsafe编译器开关来编译
MS提供了一个PEVerify.exe的程序来检查程序集的所有方法是否采用不安全代码

1.5 本地代码生成器:NGen.exe

可以在一个应用程序安装到用户计算机上时,将IL代码编译成本地代码,由于代码在安装时已经编译好了,所以CLR的JIT无需编译IL代码,提升性能
两个应用场景:
加快应用程序的启动速度:运行时不需要花时间来编译
减小应用程序的工作集
NGen生成的文件存在一下问题:
没有只是产权保护
NGen生成的文件可能失去同步(并不是一劳永逸很容易失效)
较差的执行时性能

1.6 Framework 类库

利用这些程序集可以创建一部分应用
1.Webservice
2.WebForm
3.Windows应用程序
4.RIA(Silverlight)
5.Windows控制台应用程序
6.Windows服务
7.数据库存储过程
8.组件库

1.7 通用类型系统(CTS)

基本成员
Field、Method、Property、Event
成员访问修饰符(C#)
private:只能有同一个class类型中的其他成员访问
protected:可有派生类型访问,不管那些类型是否在同一个程序集
internal:成员可由同一个程序集的任何代码访问
protected internal:成员可有任何程序集中的派生类型访问,也可由同一程序集的任何类型访问
public:任何程序集的任何类型访问
理解“代码的语言”和“代码的行为”:
使用不同的语言,用于定义类型的语法也不同,无论使用哪一种语言,类型的行为都是完全一致的,因为最终是由CLR的CTS来定义类型的行为。
eg:CTS规定类型只能单继承,但C++语言允许多继承,CTS既不能接受、也不能操作这样的类型,所以就会报错。
CTS另一条规则:所有类型必须从预定义的System.Object类型继承,System.Object类型允许做下面事情:
1.比较两个实例的相等性
2.获取实例的哈希码
3.查询一个实例的真正类型
4.执行实例的浅拷贝
5.获取实例对象的当前状态的一个字符串表示

1.8 公共语言规范(CLS)

CLR集成了所有语言,允许在一种语言中使用由另一种语言创建的对象
C#中特性:[assembly:CLSCompliant(true)]

1.9 与非托管代码的互操作性

1.托管代码能调用DLL中的非托管函数
2.托管代码可以使用现有的COM组件(服务器)
3.非托管代码可以使用托管类型(服务器)

总结:首先编译器把源代码->托管模块(包含PE32头、CLR头、元数据、IL代码)
由于CLR是不和模块们一块工作的,它只爱程序集(assembly)
编译器不得不把托管模块们合并成->程序集
于是CLR和程序集在一起了,光在一起不行还是得干活的
CLR有以小弟叫JITCompiler,它对程序集中的IL转换成本地的CPU指令存到内存中

原文地址:https://www.cnblogs.com/hailiang2013/p/2849228.html