CLR学习之初识CLR CLR基础之一---认识CLR [《CLR via C#》读书笔记]

一、什么是CLR?  

  CLR即公共语言运行时(Common Language Runtime,简称CRL),就是微软为.net产品构建的运行环境,与java的JVM类似,通俗的讲就是.net虚拟机。CLR上实际运行的并不是我们通常所用的编程语言(例如C#、VB等),而是一种字节码形态的“中间语言”。这意味着只要能将代码编译成这种特定的“中间语言”(MSIL),任何语言的产品都能运行在CLR上。CLR通常被运行在Windows系统上,但是也有一些非Windows的版本。这意味着.Net也很容易实现“跨平台”。CLR是.net系列产品运行的基础。

二、CLR与.Net Framework 的关系?

.NET框架 (.NET Framework) 是由微软开发,一个致力于敏捷软件开发(Agile software development)、快速应用开发(Rapid application development)、平台无关性和网络透明化的软件开发平台。.NET框架是以一种采用系统虚拟机运行的编程平台,以通用语言运行库(Common Language Runtime)为基础,支持多种语言(C#、VB.NET、C++、Python等)的开发。(维基百科.Net Framework)

 .Net Framework是一个支持多种开发语言的开发平台,然而这种多语言支持的特性是以CLR为基础的。

三、CLR是怎样工作的

CLR基本工作原理如下图所示:

                  

如上图所示,从源代码到可执行程序,CLR主要做了以下几件事情:

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

  托管模块(Managed Module)是一个标准的Windows可移植执行体(PE),其主要由四个部分组分组成,即PE32或PE32+头、CLR头、元数据、IL(Intermediate Language,中间语言)。

  • PE32(32位)或PE32+(64位)头

    这里描述了当前托管模块的基本信息,包括运行的Windows版本、文件类型、生成时间等。对于包含本地 CPU代码的模块,这个头包含了与本地 CPU 代码有关的信息。

  • CLR头

    包含使这个模块成为一个托管模块的信息(可由 CLR 和一些实用程序 进行解释)。头中包含了需要的 CLR 版本,一些 flag,托管模块入口方 法(Main 方法)的 MethodDef 元数据 token,以及模块的元数据、资 源、强名称、一些 flag 以及其他不太重要的数据项的位置/大小

  • 元数据

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

  • IL代码

    编译器编译源代码时生成的代码。在运行时, CLR 将 IL 编译成本地 CPU 指令

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

  CLR实际不和托管模块工作,它和程序集工作。程序集(Assembly)是一个抽象概念,在CLR的世界里,程序集就相当于“组件”。可以从以下两点对程序集有一个初步的认识:

  • 程序集是一个或多个模块/资源文件的逻辑性分组

  • 程序集是重用、安全性以及版本控制的最小单元

 为更好的理解程序集与托管模块的关系,请参考此文章:http://wenku.baidu.com/link?url=N-mICQEkfET-BvRMd1SBdffQdcpOm3cvyfFGbIS3zaRVuKYuq4Jf88JB1bROWuXFe-fE05Yow8Bb0YqO1UpNG0piJEALPh_waqe6tCySspe

 3.加载运行时程序

  生成的每个程序集既可以是可执行的应用程序,也可以是DLL(其中含有一组由可执行程序使用的类型)。当然,最终是由CLR管理这些程序集中代码的执行。Windows 检查好 EXE 文件头,决定是创建32位还是64 位进程之后,会在进程的地址空间中 加载 MSCorEE.dll 的 x86,x64 或 IA64 版本。如果是 Windows 的 x86 版本,MSCorEE.dll 的 x86 版本在 C:\Windows\System32 目录中。如果是 Windows 的 x64 或 IA64 版本,MSCorEE.dll 的 x86 版本在 C:\Windows\SysWow64 目录中,64 位版本(x64 或者 IA64)则在 C:\Windows\System32 目录中(为了向后 兼容)。然后,进程的主线程调用 MSCorEE.dll 中定义的一个方法。这个方法初始化 CLR,加载 EXE 程序集, 然后调用其入口方法(Main)。随即,托管的应用程序将启动并运行。

4.执行程序集中的代码

  到目前为止,源代码已经被编译成二进制的IL并且包含在程序集中,而且被CLR加载。但是,直接执行运算的CPU来说二进制的IL还是太高级了,而且不同的CPU支持的指令集也有所差异。因此,CLR在这里还需要对已经编译好的IL再次编译,针对CPU版本生成可以直接运行的CPU指令,这个过程是由JIT(Just In Time)编译器完成的,可以称作“即时编译”。

  当第一次执行某个函数时,MSCorEE.dll 的JITCompiler函数会从程序集的元数据中获取该方法和方法的IL,并且分配一块内存地址,然后将IL编译成的本地代码放入这块内存,然后执行内存中的本地代码。

  当再次执行这个函数的时候,由于内存中已经存在JIT编译好的本地代码,因此不需要再次进行JIT过程,可以直接执行内存中的本地代码。 可以预知的结果是,这种情况下应用程序启动后第一次调用某个模块所花费的时间要比以后调用这个模块要稍微多一些。

  现在,通过本地代码生成技术,已经可以在编译阶段就根据计算机的的实际环境生成本地代码,这样以来就可以在运行时节省JIT编译的时间,提高程序的启动效率。这看起来是一个不错的功能,但是实际上运用的不是很广泛,主要是有一下限制:

  • 编译时生成的本地代码太过于依赖本地环境,一旦环境有变化 (包括操作系统更新、.Net Framework版本更新、CPU更换等),以前生成的本地代码都不再适用。

  • 编译时生成的本地代码必须要和程序集保持同步。

  • 编译时生成的本地代码不能像运行时JIT编译那样根据运行时的情况对代码进行优化。

注:本文主要参考《CLR via C#》,同时参考了博客:CLR基础之一---认识CLR [《CLR via C#》读书笔记]

 

猜你喜欢

转载自www.cnblogs.com/LukeLee/p/10954489.html
CLR