码迷,mamicode.com
首页 > 其他好文 > 详细

The CLR’S Execution Model

时间:2015-05-26 18:19:23      阅读:198      评论:0      收藏:0      [点我收藏+]

标签:

微软.NET Framework介绍了很多概念、技术和术语。在这一章我的目标是给你一个概述,.NET Framework是怎么设计的,介绍你一些框架包含的技术,和定义的很多术语,当你开始使用.NET Framework的时候将会看到这些。我也将通过带你建立你自己的源码应用程序或者设置一个可再使用的组件(文件集)包含(类,枚举,等等)向你解释一个应用程序是将怎么执行。

 

Compiling Source Code into Managed Modules(编译源码到托管模块)

那么你已经决定使用.NET Framework作为你的开发平台了。你的第一步是决定你想要开发的是哪一种类型的应用程序或者组件。让我们来假定你已经完成了这个小细节;每一件事都已经设计好了,规格说明书都已近写好了,并且你已经准备开发了。

现在你必须决定要使用哪一种开发语言。这个任务一般都很困难,因为不同的语言拥有不同的能力。比如,非托管的C/C++,你能控制底层的系统。你能通过你想要的方式精确的管理内存,当你想创建线程的时候很容易,等等。微软Visual Basic 6.0,在另一方面,允许你快速的创建UI应用程序和可以让你容易的控制COM组件和数据库。

公共语言运行库(CLR)正如它的名字一样:runtime可用于不同的和各种各样的编程语言。CLR的核心特征(比如内存管理,程序集加载,安全,异常处理,和线程同步)可适用于任何和所有编程语言只要编译目标期间是CLR。比如,runtime使用异常报告错误,所以所有编译目标是runtime的编程语言获得错误报告都是通过异常得到的。另一个例子是runtime允许你创建线程,所以所有编译目标是runtime的编程语言都可以创建线程。

事实上,在runtime库中,CLR不知道开发者使用哪一种开发语言写的源码。这意味着你选择的开发语言应该是最容易表达你的意图的。你可以用任何你想用的开发语言只要你使用的编译器能把你的代码编程成CLR

所以,假如我说的真的,使用某个开发语言而不使用另一个开发语言有什么好处?好吧,我认为编译器作为语法检查者和"代码纠错"分析者。它们检查你的源代码,确保你写的源码有一些道理,然后输出描述你意图的代码。不同的编程语言允许你在开发时使用不同的语法。不要低估选择开发语言的价值。比如,对于数学或者财政应用程序,使用APL语法表达你的开发意图可以节省很多天开发时间相较于使用Perl语法表达相同的开发意图。

微软已经创建了几门语言编译器编译成runtimeC++CLIC#(发音"C sharp"),Visual BasicF#(发音"F sharp")Iron PythonIron Ruby,和IL汇编。除微软外,另外几家公司、大学都创建了编译器并且产生的代码目标是CLR。我知道的编译目标为CLR的编译器可编译的语言有AdaAPLCamlCOBOLEiffelForthFortranHaskellLexicoLISPLOGOLuaMercuryMLMondrianOberonPascalPerlPHPPrologRPGSchemeSmallTalk Tcl/Tk

下图指出了程序编译源码文件。如图所示,你可以使用任何支持CLR的编程语言创建源码文件集。然后你可以使用相应的编译器检查语法和分析源码。不管你使用哪一个编译器,结果都是托管组件。一个托管模块是一个标准的32位Windows PE32文件或者标准64位Windows PE32+文件并且只能在CLR上执行。顺便说一句,在Windows中托管程序集会得到DEPASLR的好处,这两个特征提高了你整个系统的安全性。

技术分享

图表描述了托管模块的组件。

 

托管组件组成部分

组件名称

描述

PE32或者PE32+ header

标准Windows PE文件头,它和COFF(Common Object File Format)的头文件很像。假如头文件使用PE32转换,那么所转换文件可在32位或者64位的Windows系统上运行。假如头文件使用PE32+转换,那么文件只能在64位版本的Windows系统上运行。头文件也规定了文件的格式:GUI,CUI,或者DLL,并且包含一个文件何时创建的时间戳。对于只包含IL代码的模块,大部分在PE32(+)头文件中的信息都会被忽略。对于包含本地CPU代码的模块,这个头文件包含了本地CPU代码信息。

CLR header

包含标记当前模块为托管模块的信息(通过CLR和工具解释得来的信息)。头文件包含所需的CLR版本,一些标记,托管模块方法(Main method)入口点的MethodDef元数据令牌。模块元数据的位置和大小,资源,强命名,一些标记,和其他少量的有趣的东西。

元数据

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

IL Coder

编译器编译产生的代码。在运行时,CLRIL编译成本地CPU指令。

本地代码编译器按照指定的CPU架构生成代码,比如x86x64,或者ARM。所有顺从CLR编译器的都会生成IL。(我在后面的章节会深入更多的IL代码细节。)IL代码有时会被归为托管代码,因为CLR管理它的执行。

除了生成IL,每一个以CLR为编译目标的编译器都要求在每个托管模块中发布所有元数据。简而言之,元数据是数据表的一个集合,描述模块中定义了什么,比如类型和成员。此外,元数据也有表指出托管模块的引用,比如导入的类型和导入的成员。元数据是老技术的一个超集,比如COM‘s Type LibrariesInterface Definition Language(IDL) 文件。重点需要注意的是CLR元数据更完整。并且,不同于Type LibrariesIDL,元数据一直是和包含IL代码的文件是关联的。事实上,元数据一直作为代码嵌入到相同名字的EXE/DLL中,使具有相同名字的EXE/DLL不能分离。因为编译器生成元数据和代码的同时把它们绑定到托管模块,元数据和IL代码不能分开描述。

下面是元数据的一些用处:

*编译时元数据移除本地的C/C++头文件和库文件需求因为所有类型/成员引用信息已经包含在IL中,IL实现了类型/成员。编译器可以直接从托管模块中读取元数据。

*微软VS使用元数据帮助你写代码。它的智能提示特性通过转换元数据告诉你方法需要的属性,事件,和提供的字段类型,在一个方法中该方法需要什么参数。

*CLR的代码验证程序使用元数据确保你的代码执行时类型安全。

*元数据允许一个对象的字段系列化为内存块,发送给另一个机器,然后反系列化,在远程机器上重建对象的状态。

*元数据允许垃圾回收器跟踪对象的生命周期。对于任何对象来说,垃圾回收器能决定对象是何种类型,通过元数据,知道哪一个对象所包含的字段被另一个对象引用。

在第二章,"生成,打包,部署,管理程序和类型",我将讲更多的元数据细节。

微软的C#Visual BasicF#,和IL Assembler总是生成包含托管代码(IL)和托管数据(回收数据类型)的模块。为了执行包含托管代码或者托管数据的模块,最终用户必须在他们的机器安装了CLR(目前作为.NET Framework的一部分),同样的,他们也需要安装Microsoft Foundation Class(MFC)库或者Visual Basic DLLs才能运行MFC或者Visual Basic 6.0程序。

默认的,微软的C++编译器生成包含非托管(本地)代码和可以操作非托管数据(本地内存)EXE/DLL的模块在运行时。这些模块不需要CLR执行。无论如何,通过指定CLR命令行转换,C++编译器生成的模块将包含托管代码,这样一来,要执行这些代码就需要安装CLR了。微软所有的编译器都提到,C++是唯一编译器可允许程序员写托管和非托管代码的编程语言并且放到一个模块中。C++也是微软编译器唯一允许开发者在源码中定义托管和非托管数据类型的语言。和其他编译器相比微软的C++编译器的灵活性是无以伦比的,因为它允许开发者使用已存在的本地托管C/C++代码并且开始集成开发者看到适合的托管类型。

 

Combining Managed Modules into Assemblies(组合托管模块为程序集)

CLR实际上不是依靠模块工作,而是依赖程序集。程序集是一个抽象概念刚开始很难明白其中的意义。首先,一个程序集是一个逻辑组对应一个或多个模块或源文件集。第二,一个程序集是可重用的、安全的、版本化的最小单元。根据你使用的编译器或工具你可以选择生成一个文件或一个多文件程序集。在CLR的世界里,一个程序集就是我们叫的组件。

在第二章中,我将十分详细的重温程序集,所以我不打算在这花太多时间在程序集上。现在我所要做的是让你意识到一个额外的概念,提供一种把一组文件当作一个实体的思路。

下图应该可以帮助解释程序集是什么。在这张图中,一些托管模块是被一个工具加工过的源文件(或数据)文件。这个工具每产生一个单独的PE32(+)文件就代表着一个经过逻辑分组的文件集。这个PE32(+)文件包含了一块数据被称作载货单。载货单是元数据表其中一个简单的集合。这些表描述了文件如何组成程序集合,公开导出类型实现的文件在集合中,并且资源或者数据文件与程序集合都有关联。

技术分享

上图指出了如何把托管模块组装到程序集。

默认的,编译器实际做的工作是把散布的托管模块转换成一个程序集合;C#编译器发出一个托管模块包含一个载货单。载货单现实一个程序集仅仅只由一个文件组成。所以,对于只有一个托管模块并且没有资源(或数据)文件的项目来说,程序集就是托管模块,当你生成程序的时候不需要任何附加的步骤。如果你要把一个文件集合分组生成程序集,那么你不得不知道更多的工具(比如程序集链接者,AL.exe)和它们的命令行选项。我将在第二章中解释这些工具和选项。

一个程序集允许你以可重用、安全的、无版本冲突的概念在逻辑和物理上解耦。你怎么划分你的代码和资源到不同的文档完全取决于你。比如说,你可以把很少用的类型或者资源放到分离的文档中,它是程序集的一部分。分离的文档在运行需要的时候会从网络上下载。假如文档从来没有被用到,该文档永远不会被下载,节省了磁盘空间和减少安装时间。程序集允许你打碎部署文件,但是依然把所有的文件看做一个集合。

一个程序集的模块也包括被引用程序集的信息(包括它们的版本号)。这个信息是一个程序集的自描述。换句话说,CLR可以确定程序集的直接依赖以便代码在程序集中能正确执行。在注册表或者活动目录域服务(Active Directory Domain Services,AD DS)中是不需要附加信息的。由于不需要附加信息,部署程序集比部署非托管组件容易多了。

Loading the Common Language Runtime(加载公共语言运行库)

每一个你生成的程序集都是一个可执行应用程序或者一个DLL(这个DLL包含一个执行应用程序的类型集合)。当然,CLR负责管理包含在这些程序集中的代码。这意味着必须在主机上安装.NET Framework。微软已经创建了一个再分布的包你可以免费下载安装.NET Framework到你的客户的机器上。一些版本的Windows已经自带了.NET Framework

你可以通过查找%SystemRoot%\System32目录下的MSCorEE.dll文件确定.NET Framework是否已经正确安装。文件存在则说明.NET Framework已经安装了。无论如何,在一台机器上可以同时安装几个版本的.NET Framework。假如你要确定哪一个版本的.NET Framework确实被安装了,检查以下的子目录。

%SystemRoot%\Microsoft.NET\Framework

%SystemRoot%\Microsoft.NET\Framework64

.NET Framework SDK包含一个叫做CLRVer.exe的命令行工具,这个工具可以显示出所有的安装在机器上的.NET Framework。这个工具还可以显示出哪一个本来的CLR正被程序使用,输入-all开关或者输入你感兴趣的进程ID以查看。

在我们开始考虑CLR怎样加载之前,我们需要花费一点时间讨论32位和64位版本的Windows。假如你的程序集文件只包含类型安全的托管代码,那么你写的代码应该可以在32位和64位版本的Windows上运行。不需要改变你的源代码以适配两个版本的Windows。事实上,编译器产生的EXE/DLL文件应该能正确运行不论是x86还是x64版本的Windows。此外,微软商店的应用程序或者类库也可以在Windows RT机器(使用ARM CPU)上运行。换句话说,一个文件可以在任何机器上运行只要在该机器上安装了相应的.NET Framework

在极限少数场景,开发者要写指定版本的Windows的代码。当使用非安全代码或者当与非托管代码交互操作的时候开发者可能需要写指定CPU架构的代码。为了帮助这些开发者,C#编译器提供了一个平台命令行开关。这个开关允许你指定是否结果集可以运行在x86机器只在32位的Windows版本运行,64位机器只运行64位Windows,或者ARM机器只运行32位Windows RT。假如你不指定平台,默认的是anycpu,这意味着结果程序集可以在任何版本的Windows上运行。VS使用者可以在项目上设置目标平台通过显示项目的属性页,点击生成标签,然后选择目标平台。

在下图中,你会发现首选32位选择框。这个选择框只有在目标平台设置为Any CPU时才会启用,并且项目是一个可执行的类型。如果你勾中首选32位,那么VS的C#编译器指定平台:anycpu32bitpreferred编译器开关。这个选项指出可执行文件应该在32位的机器上执行,即使正在64的机器上运行。假如你的应用程序不要求额外的内存以运行64位程序,那么勾中首选32位是一个有代表性的好方法因为VS不支持对x64程序编辑并继续。此外,32位程序能和32位DLL和COM组件交互操作假如你的程序需要。

技术分享

依赖于平台开关,C#编译器将会发行一个包含PE32PE32+头文件的程序集。编译器也会在头文件中发行一个需要的CPU架构(或者不可知的架构)。微软搭载了两个SDK命令行工具,DumpBin.exeCorFlags.exe,通过编译器你可以用来检查发布在托管模块中的头信息。

当运行一个可执行文件时,Windows检查这个EXE文件的头文件确定应用程序是需要32位的还是64位的地址空间。一个带有PE32头文件的文件可以在32位或者64位的地址空间运行,但是一个带有PE32+头文件的文件需要64位地址空间。Windows也检查嵌入在头文件中的CPU架构信息以确保电脑上的CPU类型匹配。最后,64位Windows提供了一种技术允许32位Windows引用程序运行。这个技术叫做WoW64for Windows on Windows 64)。

The CLR’S Execution Model

标签:

原文地址:http://www.cnblogs.com/Zhang-Xiang/p/4528502.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!