综合

img daidaoke2001

Shared Source CLI Essentials第一章第二部分

发表于2004/9/28 19:48:00  1613人阅读

分类: Rotor

共享类型系统和中间语言

       CLI中的类型在最低限度上是由字段和方法构成的,但是这些字段和方法自身又是如何定义的呢?CLI标准定义了一个与处理器无关的中间语言,用于描述程序,也定义了一个通用类型系统来为这种中间语言提供基本数据类型。这两个事物一起组成了一个抽象计算模型。标准使用一些规则来修饰这个抽象模型,这些规则描述了抽象模型如何才能转化成机器指令流和内存引用;这些转化过程的设计十分高效,能够识别和准确的描述许多不同编程语言的语义。中间语言,中间语言类型,以及转换的规则,组成了一个具有普遍意义的,用于描述程序的语言无关的方法。

       CLI规范中定义的中间语言叫做通用中间语言(CIL)。它包含一个与任何现存的计算机硬件结构均无关的丰富的操作码集合,用于驱动一个易于理解的抽象堆栈机。同样的,通用类型系统(CTS)定义了一个包含标准的跨语言互操作性的类型的基本集合。为了充分实现这种语言无关的世界的好处,高级编译器需要理解CIL指令集和它所匹配的数据类型的集合。如果没有这个协议,那么不同的语言就必须选择不同的映射方式;例如,C#中的int类型的长度有多大?它和Visual Basic中的Integer类型有什么关系?它和C++中的long类型完全相同吗?通过将指令集和这些类型进行匹配,这些选择将变得相当简单,当然,关于具体应该使用哪个指令和类型的选择是由编译器决定的,但是,一个良好的规范的出现意味着使得这些选择变得相当地直接和简单。通过使用这种方法,结果代码可以和其它语言编写的代码和框架进行互操作,从而导致了更加高效的重用。第3章详细描述了CLI类型系统,而第5章描述了CIL,以及它是如何转换成本地指令的。

 

基于类型的可移植打包单元:装配件

       CLI利用它的类型系统和抽象计算模型,实现了这样的理想:人们可以利用由不同人员在不同时间编写的软件组件,通过校验,装载这些组件,来使用它们一起创建应用程序。在CLI中,单独的组件可以打包到称为装配件的单元中,装配件可以在执行引擎中按需动态装载,既可以从本地磁盘中装载,也可以从网络上装载,甚至还可以在程序的控制下动态的创建。

       装配件为CLI定义了组件模型的语义。类型不能存在于装配件的外部。反过来说,装配件是将类型装载到CLI中的唯一的机制。装配件又是由一个或多个模块(模块是驻留信息的打包子单元),以及一大块名为装配件清单的描述装配件的元数据组成的。虽然装配件也能由多个模块组成,不过一般一个装配件都只包含一个模块。

       为了保证装配件不会在被编译和被装载的时候被篡改,每一个装配件通过一个秘钥对和整个装配件的一个哈希表来进行签名,这个签名可以被放在装配件清单中。这个签名被执行引擎所信任,并且可以保证装配件不会被篡改,以及有危险的装配件不会被装载。如果在运行时从装配件生成的哈希表和装配件清单中包含的哈希表不匹配,运行时会拒绝装载装配件,并且在潜在的危险代码有机会做任何事情之前抛出一个异常。

在许多方面,装配件对于CLI的意义,就像共享库或DLL对于操作系统的意义一样:它们都是绑定和识别属于同一个部分的代码的方法。感谢CLI中建立的可靠的对元数据和符号的绑定方法,这使得每一个组件都可以在独立于它的邻居的情况下被装载,翻译以及执行,即使它们之间相互依赖,也不会互相干扰。这是至关紧要的,因为平台,应用程序,库,以及硬件都会随着时间的变化而改变。基于组件建立的解决方案应该在这些组件变化时继续正常工作。我们将在第3章和第4章讨论装配件。

 

组件隔离:应用程序域和远程调用

       以使组件能够一起工作,并保护组件不受其它组件中的恶意代码或bug危害的方式装载各组件的能力,与在组件内部将代码组织在一起的能力一样重要。操作系统常常通过建立保护地址空间,以及提供连接保护地址空间和其它地址空间的通讯机制的方法来获得独立性;地址空间提供保护边界,而通讯机制为协作提供通道。在CLI中有相似的隔离执行代码的概念,它由应用程序域和对远程调用的支持组成。

       程序集总是在一个应用程序域的上下文中被装载的,因此类型就被它们的应用程序域限制了范围,例如,程序集中定义的静态变量在应用程序域中分配空间和存储。如果同一个程序集在三个不同的域中被加载,会为这个程序集中类型的数据分配三个不同的拷贝。在本质上,应用程序域是轻量级的地址空间,对于在各个应用程序域之间传递数据,CLI执行和操作系统在不同的地址空间之间传递数据所执行的相同的限制。希望跨越域边界进行通讯的类型,必须使用特殊的通讯通道,并按照特定的规则来进行操作。

       被称为远程调用的技术,可以用来在不同的物理计算机(计算机上可能运行不同的操作系统,具有不同的处理器)上运行的应用程序域之间进行通讯。就像经常一样,远程调用机制常常用于分隔位于同一个机器中同一个进程的域中的各组件。希望参与到远程调用中的组件要么必须是可序列化的,这样它们就能在域之间传递,要么必须继承自System.MarshalByRefObject类型,这样它们可以使用负担传递工作的代理对象进行通讯。在第4章中会讲述应用程序域,远程调用,以及装载的详细内容。

 

为灵活的版本装载服务的命名规则

       因为所有的类型和类型的代码都存在于程序集中,因此必须要有一组定义明确的描述当执行引擎需要程序集中的类型时如何查找和使用程序集的规则。程序集的名称由一个元素的标准集合组成,包括程序集的基础名称,一个版本号,一个地区文化(为全球化服务),以及一个代表发布这个程序集的发布者的公钥的哈希表。组合名称保证了由各程序集创建的软件会优雅的适应版本的变化。编译时,每个程序集都会包含在编译时它所依赖的其它程序集的组合名称的引用,并记住每个这些程序集的版本信息。这样,当装载时,程序集会非常明确的要求它所依赖的程序集的某个特定(或语义一致)的版本。用来满足这些需求的绑定策略可以通过对配置的设置来改变,但是绑定策略是不可能被忽略的。

       通常可以在下列两个地方之一找到程序集:在一个被成为全局程序集缓存(GAC),作用于整个机器的缓存,或者一个基于URL的查找路径。GAC是每个机器上的程序集的有效的数据库,每一个程序集都由它的四部分名称来唯一的识别。GAC可以是一个文件系统的目录,但也可以不是,CLI的运行必须能够在GAC中存放同一个程序集的不同版本,并且能够跟踪这些不同版本。查找路径基本上是一个URL(通常是文件系统的目录)的集合,当需要装载一个程序集时,会搜索这些路径。第4章会详细讲述装载的过程,以及装载是如何实现的。

 

JIT编译和类型安全

       CLI描述的执行模型意味着编译高级类型的工作应该和将这些类型描述转换为基于特定处理器的代码和内存结构的工作分离开来。这种分离为计算模型带来了很多重要的优势,例如能够在出现了新的操作系统和处理器后,很容易的调整代码来适应,以及能够独立的定义来自各个不同来源的组件的版本。这种分离也带来了新的挑战。例如,因为所有的类型都由CILCTS来描述,所以,所有的类型都必须在它们能够被使用前翻译成机器码和内存结构;本质上,整个应用程序总是必须在能够运行前被重新编译,这可能会是一个非常昂贵的方法。

       为了分次消耗将CIL转化成机器指令的代价(即包括装载所花费的时间,也包括所需要的内存),基于CLI的应用程序的类型的装载方式很独特,直到软件的运行需要它们时才会被装载,一个类型一旦被装载,它的各个方法会直到软件的执行需要它们时才被翻译。这种延期的装载和代码生成被称作即时(JIT)编译。CLI并不是一定要求最后时刻的JIT编译,但是延迟装载和编译总是会发生在一个应用程序的生命周期的某些点上,来将CIL转化为机器码。可以想象的一种情况就是,软件的安装程序就可以执行编译。第5章将讲述为了符合CLI规范JIT编译需要实现的方法。

       之所以要在CLI执行模型中要建立JIT编译的最重要的原因并不明显。在执行引擎自己的装载器和编译器的控制下进行从抽象组件到可运行的机器码的转换,是使得执行引擎在运行时保持控制,以及高效的运行代码的原因,即使是在c++编写的代码和一个托管语言编写的代码之间进行来回调用,执行引擎也能很好的工作。传统的编译,链接,和装载的过程,在CLI中依然存在,但是,就像我们看到的一样,每个工具链上的元素必须大量使用复杂的技术(例如缓存),因为延迟的使用导致了较高的运行时代价。这些较高的代价是值得忍受的,因为延迟也使得能够对运行的组件的行为进行全面控制。因为CLI的执行是基于对类型的逐渐加载,以及所有的类型都是使用平台无关的中间语言定义的,所以CLI执行引擎在它运行的过程中是在不断的编译和添加新的行为的。因为CIL被设计成可校验的,和类型安全的,编译成机器码的过程是在具有特权的执行引擎的控制下执行的,所以可以在允许一个新类型运行之前校验类型安全性。安全策略也能够在CIL被转换成机器码时检查和应用,这就意味着安全检查可以直接插入到代码中,在方法被执行时以系统的名义来执行。简而言之,通过使用延迟加载,校验和直到运行时才对组件进行编译这些技术,CLI可以加强可靠的托管执行。

 

托管执行

       类型装载是导致CLI的工具链在运行时十分忙碌的关键所在。看看装载进程的部分工作,CLI需要编译,装配,链接,验证可执行文件的格式和程序的元数据,校验类型安全,最终甚至是管理运行时的资源,例如内存和处理器周期,在它的控制下代表组件运行。将所有这些阶段联系在一起的任务导致CLI包括了名称绑定,内存分布,编译和打补丁,分隔,同步化,以及符号解析的基础结构。因为总是希望这些元素的执行延迟到尽可能最后的时刻,所以执行引擎可以享受对装载和执行策略,内存组织,生成代码,以及代码和底层平台、操作系统的交互方式的高度可靠的控制。

       延迟编译,链接,和装载为跨越目标平台和跨越版本变化提供了更好的可移植性。通过延迟排序和排列的决定,延迟地址和偏移量的计算,延迟处理器指令的选择,延迟调用的转化,当然,还有延迟链接到平台的自身服务上,程序集可以变得更加的向前兼容。由定义良好的元数据和策略驱动的延迟过程,是非常有活力的。

       翻译元数据的执行引擎是可信赖的系统代码,因此,通过后期装载,安全性和稳定性也得到了增强。每个程序集都包含一个与它相关的许可权限的集合,定义了允许这个程序集执行什么操作。当这个程序集中的代码企图执行一个敏感的操作时(例如企图读或写一个文件,或者企图使用网络),CLI会查看调用堆栈,检查它来判断是否当前范围内的所有代码都有合适的权限――如果堆栈上的代码没有合适的权限,操作就会被拒绝,一个异常会被抛出(异常是另一个能够在组件之间进行简单的交互的机制;CLI的设计不仅支持在执行引擎内广泛范围内的异常语义,也紧密的集成了来自底层平台的异常信号)。第6章和第7章将详细描述托管执行。

 

      使用元数据来实现数据驱动的可扩展性

       CLI组件是自描述的。一个CLI组件包含了它内部的每个成员的定义,而这些定义信息在运行时中受保证的有效性是帮助虚拟执行具有高度可适应性的一个因素。每个类型,每个方法,每个字段,每个单一方法调用上的每个单一的参数必须是被充分描述的,而这个描述信息必须存储在程序集内部。因为CLI将各种链接操作延迟到必需执行的最后时刻,那些希望通过使用元数据来操作组件和创建新组件的工具和程序获得了极大的灵活性。建立在CLI之上的代码可以使用与CLI所使用的相同类型的技巧,对于工具软件和运行时服务来说,这简直就是天降横财。

       为了获得类型的信息,CLI程序员可以使用执行引擎提供的反射服务。反射提供了在运行时检查编译时信息的能力。例如,对于一个托管组件,开发者可以获得类型的结构信息,包括它的构造器,字段,方法,属性,事件,接口,以及继承关系。可能更重要的是,开发者可以使用名为定制属性的功能将自己的元数据添加到组件的描述信息中。

      通过反射不仅可以获得编译时信息,而且可以操作运行中的实例。开发者可以使用反射来进入类型内部,获得它们的结构信息,并基于这个结构信息操作类型的内部信息。对于方法来说,是一样的;开发者可以在运行时动态的调用方法。这种元数据驱动风格的编程能力,以及如何才能实现它的问题,我们会在第3章接触到,并且在第8章会详细的进行讲述。

 

CLI的共享源码实现:Roter

       2001年夏天,雷德蒙公司的一小组开发人员宣布了一个微软公司少有的开发计划:一个免费使用的软件产品,包括可修改,可重新发布的源代码。这个叫做SharedSource CLI(SSCLI,人们也亲切的称呼它的代码名称,“Rotor)的产品,包括一个实现了全部功能的CLI执行引擎,一个C#编译器,基础的编程库,和许多相关的开发工具。这个产品在商业的.Net框架周边静悄悄的发展,代表了微软的开发者工具策略的一个重要方面,实际上,SSCLI要实现三个目的:检验CLI规范的可移植性,帮助人们学习和理解微软的商业CLR产品,以及长期促进学院对于CLI的兴趣。最重要的是,SSCLI要符合ECMA标准,以便任何希望了解和实现这个标准的人可以将SSCLI作为一个指南。

       虽然名义上SSCLI是这本书的主题,但实际上CLI标准才是本书的核心。SSCLI帮助我们阐明CLI是怎样一件如此令人感兴趣的工作,以及为什么。这个产品本身包含了巨大数量的代码,因此,它可以为在开发工具或系统设计领域工作的研究者和试验者提供重要的帮助,对于那些教授计算机科学的老师也是一样。本书尝试通过提供远多于CLI理论的知识,来辅助分析和解释基本代码的规则,以便为这些人提供一个针对这些代码的高级指南。CLI标准会变得越来越重要,而完全理解它的最好方法,就是浏览,创建,观察和分析一个运行中的实例。

       Rotor向我们演示了创建一个可移植,编程语言无关的CLI规范的实际版本的一种方法,当然,它不是唯一的方法。在写作本书的时候,就存在着其它的CLI规范的实现方式,包括微软公司的两种(商业.Net框架和名为“精简框架”的运行在微型设备上的版本),以及第三方厂商的两种开源的实现,一种来自Ximian公司(名为Mono),另一种来自DotGNU项目(名为Portable.NET――可移植.NET)。Rotor自身提供了大大超过标准的各种附加开发工具和功能。为了说明在这个产品中包含了那些内容,图13使用一张图来说明微软商业产品(.NET CLR),CLIC#标准,以及Roter之间的区别。

       如图所示,SSCLICLI标准的一个超集,而微软的商业产品又是SSCLI的一个超集。

       Rotor是一个由许多人历经多年开发的巨大数量的代码的集合,因此,它是一个复杂和

文体上的变量(stylistically variable???),就软件的大小来说,它可以与那些最大的著名开源产品相比,例如Xfree86Mozilla,以及OpenOffice。和这些产品一样,如果希望开始了解这些巨大数量的代码,可能会令人望而却步。本书将帮助您较为容易的完成这个任务,让我们从这个产品自身的简短历程开始吧。

       SSCLI是使用C++C#,以及少数处理特定处理器细节的汇编组合编码开发的,这个产品通过一个三阶段的过程开发完成。首先,一个基于特定平台的C++编译器被用来开发一个平台适配层(PAL),这是一个用于将操作系统的各API的差别隐藏在一个单一抽象编程集合后面的库。之后,一系列创建SSCLI所必需的开发工具(包括C#编译器)在PAL库上被创建和链接。最后,使用这些工具和PAL库来开发产品的其它部分。

 

       11列出了一些在浏览SSCLI源码时有用的子目录,SSCLI源码位于本书的随书光盘中(也可以从http://msdn.microsoft.com/net/sscli处下载

11。产品中重要的子目录,以及各目录的内容。

子目录                                     内容

/build                                  包括创建好的可执行文件和库文件

/clr/src                               许多包含核心内容子目录的主目录

/bcl                                           基础类库,用C#编写

/csharp                               一个C#编译器,用C#编写

/classlibnative                      C++编写的开发库

/debug                                实现托管调试

/dlls/mscorsn                             强名称加密代码

/fjit                                     SSCLIJIT编译器

/fusion                                定位版本文件的代码

/ilasm                                 一个CIL汇编器

/ildasm                               一个CIL反汇编器

/inc                                           共享的包含文件

/md                                    元数据工具

/toolbox/caspol                    caspol安全工具的源码

/tools                                  许多工具程序的主目录

/clix                                   SSCLI托管执行的启动程序

/gac                                   gacutil缓存工具的源码

/peverify                             peverify CIL校验工具

/sos                                    SOS调试外挂库

/strongname                        序列号代码签名工具

/vm                                    CLI执行引擎

/docs                                  文档

/fx/src                                附加的托管库的主目录

/net/system/net                    网络功能库

/regex/system/text                      正则表达式库

/jscript                                一个可以编译成CIL代码的完整Jscript编译器,使用 C#编写 (一个托管的托管代码编译器!)

/managedlibraries/remoting   bcl目录中的基础类库附加的远程调用支持

/pal                                           PAL在多个特定操作系统上的实现

/palrt                                  支持SSCLI运行的低级API ,但不针对某个特定的操作系统

/samples                                    使用CLI的例子程序

/tests                                  扩展测试和测试基础结构

/tools                                  用来创建SSCLI产品的工具

 

       这些子目录可以被分成四个概念明显不同的部分,如下所示:

n         CLI执行引擎

n         包装和扩展了执行引擎的组件框架

n         用于从一个操作系统移植到另一个操作系统的可移植层(PAL)

n         工具,测试,编译器,文档,以及为和托管代码一起工作服务的工具。

 

让我们依次钻研这些部分,关注哪里能够找到它们的实现方法。

 

CLI执行引擎

执行引擎是CLI的核心。它包括组件模型,以及运行时服务,例如异常处理,自动的堆和栈的管理。在许多方面,它简直是一个大巫师(???);它就是当我们谈论“运行时”和“虚拟执行环境”时所指的代码。JIT编译,内存管理,程序集和类的装载,类型解析,元数据的解析,堆栈遍历,以及其它的基础机制都是在这里实现的。在sscli/clr/src目录和四个子目录vm,fjit,md,fusion中可以找到包含了大部分执行引擎功能的代码。

       如图14所示,执行引擎由一系列可装载的动态库组成,而不是一个单一的可执行文件。Clix程序启动器(或者任何希望使用执行引擎服务的程序)装载主要的共享库,sscoree,来在进程中创建一个CLI的实例,然后为这个实例提供一个需要执行的启动程序集。因此,在执行引擎中是没有main函数的,它是以打包的形式来被其它程序寄宿的。执行引擎依赖大量的其它共享库,包括一些不封闭的库,因为它们是需要被替代代的,例如crypto代码必须装载和创建位于mscorsn中的签名汇编,以及一些会在许多不同的地方潜在的发挥作用的库,例如PAL,可以在rotor_pal目录和rotor_palrt目录中找到它。最后,那些不常需要的代码也被打包在分别装载的库中,例如mscordbc,这个库实现调试器功能的支持。

 

CLI中的开发库

       CLI的共享框架不仅包含标准的,底层的功能,例如元数据,通用中间语言,以及通用类型系统,也包括高级的,面向生产的类库。表12通过列举有用途的各方面简单的总结了这些库的内容

12. CLI标准库所包含的高级元素。

类别                                        功能

生产库                                    文本格式化,正则表达式,集合,时间,日期,文件和网络IO,配置,诊断,全球化,独立存储,XML

执行引擎库                            分隔的域,异步调用,堆栈遍历,堆栈跟踪,垃圾回收,句柄,环境,线程,异常,基于监控的同步,安全性,校验,反射,序列化,与机器码的互操作

类型相关的库                         简单类型,值类型,委托,字符串,数组

扩展的数值库                         Decimal数值, 双精度和单精度浮点数,数学计算

编程语言支持            编译器服务,定制元数据属性,资源回收

 

       这些库提供了一种使用底层操作系统功能的接口,不过是通过一种与CLI的服务和规范相适应的方式来使用的,这样通过它们的一致性和高质量,就提高了程序员的生产力。

       这些API也为其它一些不太重要的方面服务:它们通过暴露编程服务和规则促进了组件集成,通过编程服务和规则的使用促进了组件的卫生学。将组件开发者必须完成的薄记工作降低到最小的服务,或将复杂的组件间管理协议的需要降到最小的那些服务,使组件之间的继承更加平滑和安全(并且减少了需要编写的代码)。一个组件越不依赖于其它组件,这个组件必须为其它组件作的工作就越少,这个应用程序因此也就越可能远离BUG,易读和健壮。我们意识到基于组件的软件真正的含义,必需将组件创建为依赖一个以这些原则为核心设计的环境中的托管执行。

       有人可能会将CLI库想象成C运行时库的另一个时髦版本。其实CLI库并没有试图为所有的程序员提供所有的功能;而是提供一组几乎每一个程序员都会用到的核心组件。因为sscli/clr/src/bcl目录下的基础库,是任何CLI实现所必须具有的部分,所以它们形成了可移植的应用程序实现的基础。在sscli/fx,sscli/clr/src/classlibnative,sscli/managedlibraries目录下的附加库,要么是可选的标准库,要么就是sscli中的部分。目前,Sscli中所有的库都可以在微软的商业.Net框架中找到。

       如果浏览各个程序库,就会发现,除了sscli/docs目录下有专门说明Rotor产品和它的功能的文档之外,还有一个单独可下载的文件(也包含在本书的随书光盘中),其中包含了类库的文档。该文档来自微软的.Net框架SDK中使用的文档,不过它已经经过了修改,并转化成简单的HTML文件了。

 

平台适配器层

       PAL是一个很有趣的软件,虽然人们并不太关注它,但它十分有用。当然,任何包含大量代码的适配器或驱动层都有一个显著特点,就是可以运行在许多操作系统平台上,PAL     的首要目的是将实现从各种操作系统的细节中分离开来。Sscli中的选择十分明确:因为它是从特定的Win32代码启动的,因此PAL被设计为表达Win32API的一个子集(可以在sscli/pal/rotor_pal.h中找到)。这个实现绝对不会完整,因为它只需要提供CLI实际会使用的那些调用。不要企图将PAL用作一个通用的win32模拟层,因为它是不完整的。

       当然,将Rotor移植到新平台的工作应该从PAL开始,因为用来创建Rotor的工具依赖于基于它们的操作系统和资源的PAL。可以查看sscli/pal/unix目录,来了解包含了什么内容。这里包含了大量必须完成的重要工作:提供通用异常处理机制,通用线程,共享的句柄管理,IO,同步,调试等等。特定的主机进程,例如web服务器或者数据库,很可能会需要它们自己相似的运行时。PAL的语义需要考虑这些问题。因为上面这些情况,以及PAL定义了如何使用操作系统资源的方法,所以对于很多人来说,理解各种各样的PAL的实现会十分重要和有用。

       除了PAL之外,名为sscli/palrt/src目录下包含了一个Win32 API的实现库,SSCLI需要这个库,但这个库的执行并不依赖于操作系统。这个库也包括了少量PALAPI。它确实是一个各种功能的大杂烩,不过有趣的是,其中包含了decimal算法,一些微软的COM组件模型的简单实现,数组处理,内存管理,以及各种其它有用的函数。

       PAL最有趣的方法一定就是执行引擎控制了。SSCLI的设计要求能够在原始进程中和机器码进行协作。这意味着需要捕捉许多操作系统调用,以便给执行引擎提供机会,来维护使用运行时系统所需的薄记信息,例如垃圾回收或者安全系统。这是PAL层一个关键的用处,SSCLI实现是以PAL所表达的抽象方式创建的,如果没有PAL,它就不能保持独立,安全和控制。例如,线程和异常处理都是在PAL中实现的,二者对于运行时的执行引擎都很关键,因为它使用异常帧来跟踪托管模块,使用线程堆栈来存储包括自身许多服务状态的扩散结构。PAL这方面的细节会在第6章中详细讨论,而PAL的自身设计将是第9章的主题。

 

工具,编译器,测试,文档和其它有用的东西

       Rotor中相当一部分代码是由用于创建,测试,和使用它的CLI实现的框架结构组成的。我们刚讨论过的PAL层,就是这样的代码。在这个产品的各个地方都可以发现各种附加的开发工具,有用的东西,以及测试程序。这些工具属于管理开发和创建产品的广泛范围。

       就管理开发来说,Rotor产品中的许多工具会让任何有过使用微软.Net框架经验的程序员感到很熟悉,因为这两个实现共享它们基础的功能集,例如链接器,汇编器,以及反汇编器。sscli/clr/src, sscli/clr/src/tools, and sscli/clr/src/toolbox目录包括了这些功能的子目录,以及只用于使用SSCLI来开发和运行托管代码的工具,例如clix.exe。程序员应该查看sscli/docs目录下的文档来了解是否某个特性是由Rotor版本的工具和它的.Net框架的副本所共享的,不是所有的特性都是共享的。

       用于自我生成Rotor的创建系统位于sscli/tools目录下。这些工具在PAL上创建,用于跟踪依赖关系,驱动创建进程,汇编库和执行文件,一旦被创建,装配库和可执行文件将被放到sscli/build目录下。Rotor中的依赖令人费解,因为它是十分巨大的项目,因此这些工具十分重要。可以查看sscli/docs/buildtools目录来了解如何使用它们,以及当开发者在修改代码时如何与它们交互。

一旦SSCLI被创建,就可以使用sscli/tests目录下的测试程序来测试它了。特别要说明的是位于sscli/tests/palsuite目录下的PAL测试程序,可以用它来校验新的PAL实现,或者将它校验一个现存的PAL中的改变,在sscli/tests/bvt目录下的开发者创建校验测试(BVT),可以用来检查在执行引擎中完成的工作。还有为其它领域(例如基础类库)服务的测试,例如:基础类库。大部分测试,包括BVTs,使用了sscli/tests/harness目录下的测试工具,文档位于sscli/docs/testing_overview.html中。

Rotor相关的文档和技术资料位于sscli/docs目录下,这个目录包含了对于浏览源码,修改代码,以及理解CLI结构和创建SSCLI时特定实现的选择很有用的信息。该目录下也包括一个PAL的详细说明,对于需要将Rotor移植到新平台上的人会非常有用。花费一些时间来浏览这个目录是很值得的。

 

本书范围

       本书关注的重点在于在SSCLI中是如何实现CLI组件模型和它底层的执行引擎的。也简洁的讲述了将这样一个机制放置在操作系统上的需要,以及通用移植的事项。而关于编译器,语言和框架,以及非面向组件的使用CLI的方法的论述就比较少了,不过这些内容可以在众多的讲述.Net框架和CLI的书中找到。书的大小和范围,以及作者希望看到本书出版的事实,决定了本书最关注的内容。

       还需要声明一点:本书中大量来自sscli源码的C++例子程序大部分都被清除了,用进程中的伪代码来代替。这样做的目的是移去那些会混淆真正代码的丑陋的宏定义,错误处理以及断言,以使代码更加可读。如果您计划添加和修改sscli的源码,你应该意识到那些必须保留的变量,以及采用与SSCLI开发者相同的编程风格和错误处理方法。附录D对这些方面做了简短的描述。

 

摘要

       CLI是第一个从头设计,可以被许多不同的编程语言共享的虚拟执行环境。平台提供者,框架创建者,以及程序员不必被迫关注所有的语言特性,而只需利用创建基于组件的计算工作的优势,例如,异常,垃圾回收,反射,代码访问安全,数据驱动的可扩展性。使用CLI,可以很容易的将已经存在的代码合并到基于组件的编程工作中,这就导致了可增长的互操作性和共享的框架。

       CLI用于打包,描述和部署组件的标准格式与操作系统和实现语言均无关。这是很重要的,因为这种格式形成了CLI数据驱动的结构的基础。数据驱动机制提高了程序员的生产力,因为它使得不同的程序,库,和工具能够无缝的进行交互,并随着时间的推移而发展。一个数据驱动的组件模型就如同今天的技术所认为的那样,是将来的保证。

       虚拟指令集和类型系统描绘了CLI的虚拟执行模型,展示了圣杯诱人的光芒:软件可以在任何地方运行,CLI设计当然预见到了一个各种不同的实现和不同版本的标准可以在许多平台上并存运行的世界。然而在这个世界中,利用CLI对互操作性的优秀支持,每一个实现都可能暴露唯一的框架,服务,功能和工具,或者增大了基础容量的语言特性。这将导致类似C语言的开发,在这种开发中,很少会有重要的应用程序是独立的在标准运行时上创建的。取而代之的是,应用程序会明智的将平台相关的各库的标准功能,和为跨平台使用特别设计的库组合起来。许多重要的CLI程序会将平台相关的组件和为跨平台使用特别设计的第三方组件组合起来形成标准的组件。

       人们开发出CLI的语言无关特性,数据驱动的结构,以及它的虚拟执行模型,以创建一个组件之间能够高效的协作,而不需牺牲安全性和封装性的舞台。元数据伸展的链创建了一个能够意识到组件的行为,在运行它们之间能够将安全错误注入它们的代码中的环境。CLI执行模型中的每一个阶段都包括了从前一阶段接受数据,转换和增强它,然后将它传递给下一个阶段的工作。本书描述了这些阶段的完整链,以及它们基于实现的执行引擎,从最初的启动序列开始,直到最后它的托管资源被释放为止。

阅读全文
0 0

相关文章推荐

img
取 消
img