原文: http://www.tracefact.net/CLR-and-Framework/DotNet-Framework.aspx
CIL:公共中间语言(Common Intermediate Language),由于程序集是由CIL语言所描述的,因此CIL也叫做程序集语言(Assembly Language)
Assembly: 程序集
-----------------------------------------------------------
C#,VB.Net等语言经过编译后生成程序集(CIL代码)
C#源程序 --> C#编译器 --> 程序集(CIL代码)
VB.NET --> VB.NET编译器 --> 程序集(CIL代码)
Delphi.NET --> Delphi.NET编译器 --> 程序集(CIL代码)
------------------------------------------------------------
CIL最初是随着.NET由微软一起发布的,因此之前也叫做MSIL(Microsoft Intermediate Language),后来进行了标准化,之后便被称做CIL.
------------------------------------------------------------
接下来再深入地分析一下,公共中间语言这个术语到底包含了哪几层含义。
- 公共。因为不论是C#语言也好,VB.NET语言也好,C++/CLI语言也好,甚至是重新开发的一套以自己的名字缩写命名的语言,只要它期望运行的目标平台是.NET,在经过相应的编译器编译之后,所生成的程序集就是由CIL语言代码描述的。
- 中间。这个词也是大有深意,为什么不叫公共机器语言(Common Machine Language),或者公共本地语言(Common Native Language)?因为这种语言只是比我们使用的高级语言,比如C#低级一点,并不是CPU可以直接执行的本地机器语言。这种语言还需要.NET运行时(.Net runtime)环境的支持,在执行之前,进行一个被称为Just-in-time(即时)的二次编译过程,才能转变成计算机可以识别的指令。关于.NET运行时,以及详细过程后面再介绍,现在只要知道,这个文件所包含的CIL代码并非机器可以直接执行的指令代码。
- 语言。CIL不过是一种程序语言,只不过相对于C#来说,它是一种更低级语言。CIL是一种基于堆栈的语言,同时,它提供了class、interface、继承、多态等诸多面向对象的语言特性,因此它又是完全面向对象的语言。如果愿意,甚至可以直接编写CIL代码,并且使用CIL的编译工具IL ASM(IL Assembler,IL汇编程序)来对它进行编译。只不过,和大多数低级语言一样,这种方式会使开发效率会变得很低。这里注意区别一下IL ASM和IL DASM,它们的拼写是不同的。
------------------------------------------------------------
BCL: Base Class Library 基类库 (相当于Delphi的RTL Runtime Library),程序集位置:C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\mscorlib.dll,使用对象浏览器(Visual Studio菜单→视图→对象浏览器)来查看mscorlib.dll程序集
------------------------------------------------------------
基元类型(Primitive Type):int, string byte等由编译器直接支持,将语言本身的关键字类型转换为CIL类型
类类型(Class Type):如Console等没有直接映射到IL的关键字的普通类
------------------------------------------------------------
FCL:Framework Class Library框架类库(相当于Delphi的VCL Visual Component Library), BCL为FCL的一个子集
从功能上来看,可以将FCL框架类库划分成以下几层。
- 最内一层,由BCL的大部分组成,主要作用是对.NET框架、.NET运行时及CIL语言本身进行支持,例如基元类型、集合类型、线程处理、应用程序域、运行时、安全性、互操作等。
- 中间一层,包含了对操作系统功能的封装,例如文件系统、网络连接、图形图像、XML操作等。
- 最外一层,包含各种类型的应用程序,例如Windows Forms、Asp.NET、WPF、WCF、WF等。
------------------------------------------------------------
CTS:Common Type System公共类型系统.CTS规定了可以在语言中定义诸如类、结构、委托等类型,这些规则定义了语言中更高层次的内容。1)C#只实现了CTS 的一部分功能。也就是说,CTS规范了语言能够实现的所有能力,但是符合CTS规范的具体语言实现不一定要实现CTS规范所定义的全部功能。2)C++/CLI又被约束为只能继承自一个基类,换言之,C++中的部分功能被删除了。,就是说,任何语言要符合CTS,其中与CTS不兼容的部分功能都要被舍弃。
由于CIL是.NET运行时所能理解的语言,因此它实现了CTS的全部功能。虽然它是一种低级语言,但是实际上,它所具有的功能更加完整.
------------------------------------------------------------
CLS:Common Language Specification公共语言规范.CLS为CTS的一个子集
如果利用C#开发的一个程序集的公开部分仅采用了CLS中的特性,那么这个程序集就叫做CLS兼容程序集(CLScompliant assembly),
FCL框架类库大多符合CLS
语言特性(language features):是否区分大小写,标识符的命名规则如何,可以使用的基本类型有哪些,构造函数的调用方式(是否会调用基类构造函数),支持的访问修饰符等.
检验程序集是否符合CLS:[assembly:CLSCompliant(true)]
------------------------------------------------------------
CLR: Common Library Runtime公共语言运行时,别名VES(Virtual Execution System,虚拟执行系统)
CLR是一个软件层或代理,它管理了.NET程序集的执行,主要包括:管理应用程序域、加载和运行程序集、安全检查、将CIL代码即时编译为机器代码、异常处理、对象析构和垃圾回收等。相对于编译时(Compile time),这些过程发生在程序运行的过程中,因此,将这个软件层命名为了运行时,实际上它本身与时间是没有太大关系的。有一些朋友在初学.NET的时候,纠结在了Runtime这个词上,总以为和时间有什么关系,总是不能很好地理解CLR。笔者认为重要的是理解CLR是做什么的,而不用过于关注它的名称。
------------------------------------------------------------
CLR作用:
由于CLR本身用于管理托管代码,因此它是由非托管代码编写的,并不是一个包含了托管代码的程序集,也不能使用IL DASM进行查看。它位于C:\%SystemRoot%\Microsoft.NET\Framework\版本号下,视安装的机器不同有两个版本,一个是工作站版本的mscorwks.dll,一个是服务器版本的mscorsvr.dll。wks和svr分别代表work station和server。
接下来再看一下CLR是如何运行起来的。虽然从Windows Server 2003开始,.NET框架已经预装在操作系统中,但是它还没有集成为操作系统的一部分。当操作系统尝试打开一个托管程序集(.exe)时,它首先会检查PE头,根据PE头来创建合适的进程。
接下来会进一步检查是否存在CLR头,如果存在,就会立即载入MsCorEE.dll。这个库文件是.NET框架的核心组件之一,注意它也不是一个程序集。MsCorEE.dll位于C:\%SystemRoot%\System32\系统文件夹下所有安装了.NET框架的计算机都会有这个文件。大家可能注意到了,这个库安装在System32系统文件夹下,而没有像其他的核心组件或类库那样按照版本号存放在C:\%SystemRoot%\Microsoft.NET\Framework\文件夹下。这里又存在一个“鸡生蛋问题”:根据不同的程序集信息会加载不同版本的CLR,因此加载CLR的组件就应该只有一个,不能再根据CLR的版本去决定加载CLR的组件的版本。
MsCorEE.dll是一个很细的软件层。加载了MsCorEE.dll之后,会调用其中的_CorExeMain()函数,该函数会加载合适版本的CLR。在CLR运行之后,程序的执行权就交给了CLR。CLR会找到程序的入口点,通常是Main()方法,然后执行它。这里又包含了以下过程:
- 加载类型。在执行Main()方法之前,首先要找到拥有Main()方法的类型并且加载这个类型。CLR中一个名为Class loader(类加载程序)的组件负责这项工作。它会从GAC、配置文件、程序集元数据中寻找这个类型,然后将它的类型信息加载到内存中的数据结构中。在Class loader找到并加载完这个类型之后,它的类型信息会被缓存起来,这样就无需再次进行相同的过程。在加载这个类以后,还会为它的每个方法插入一个存根(stub)。
- 验证。在CLR中,还存在一个验证程序(verifier),该验证程序的工作是在运行时确保代码是类型安全的。它主要校验两个方面,一个是元数据是正确的,一个是CIL代码必须是类型安全的,类型的签名必须正确。
- 即时编译。这一步就是将托管的CIL代码编译为可以执行的机器代码的过程,由CLR的即时编译器(JIT Complier)完成。即时编译只有在方法的第一次调用时发生。回想一下,类型加载程序会为每个方法插入一个存根。在调用方法时,CLR会检查方法的存根,如果存根为空,则执行JIT编译过程,并将该方法被编译后的本地机器代码地址写入到方法存根中。当第二次对同一方法进行调用时,会再次检查这个存根,如果发现其保存了本地机器代码的地址,则直接跳转到本地机器代码进行执行,无需再次进行JIT编译。
可以看出,采用这种架构的一个好处就是,.NET程序集可以运行在任何平台上,不管是Windows、UNIX,还是其他操作系统,只要这个平台拥有针对于该操作系统的.NET框架就可以运行.NET程序集。
------------------------------------------------------------
CLI: Common Langauge Infrastructure 公共语言基础,CLI包括:CIL、CTS、CLS、VES、元数据、基础框架。.NET框架是CLI标准的具体实现,Mono Project就是CLI标准的另一个实现