1.2 Rotor:一个CLI的共享源代码实现
1.2.1.
2001的夏天,微软雷蒙德总部的一个team宣布要启动一个难得的项目。这个项目就是Shared Source CLI(SSCLI,代号“Rotor”),项目的范围涵盖了一个全功能的CLI执行引擎、C#编译器、基础类库以及一系列相关开发工具。和商业版本的.NET框架一样,它们都是微软开发工具战略的重要部分。SSCLI有三大目标:验证CLI标准的可以执性;帮组人们更好的理解商业版本的.NET;使学术界对CLI保持长期的兴趣。最重要的是,SSCLI是完全符合ECMA标准的,这为任何希望实现CLI标准的人提供了指南。
虽然主题讲的是SSCLI,不过CLI标准才是核心内容。SSCLI只是用来帮助我们更好的理解CLI标准的how和why。本书试图为需要在CLI理论之上理解和hacking代码的人提供指南。在将来CLI会变得更加重要,没有比直接浏览、构建、观察并进行调整CLI实现更好的方式来理解它了。
Rotot展示了一种构建可移植地、语言无关的CLI的方式,当然这绝不是唯一的方式。在本书的写作时,微软已经发布了两个CLI实现(商业版的.NET Framework以及Compact Framework)。还有两个第三方的开源实现,一个来自Ximian(Mono),还有一个来自DotGNU项目(Portable.NE)。相较而言Rotor提供了更丰富的开发工具,并且在标准的基础之上提供了更多的特性。图1-3展示了Rotor和微软商业版本CLI(.NET CLR)以及C#之间的差异。
如图1-3所示,SSCLI是CLI的一个超集,而微软所提供的商业版本的CLI又是SSCLI的一个超集。
Rotor本身是一个开发了多年的大项目,拥有众多代码,体系结构复杂多变。在规模上,Rotor可以和任何类似的开源项目如XFree86、Mozilla还有OpenOffice相媲美。其代码量庞大地让人望而却步。所以本书将帮助我们更容易的上手。
图 1-3 Shared Source的组件
SSCLI同时使用了C++和C#混合编写而成。要构建他的话,需要分三步进行。首先,使用C++编译器对用来隐藏底层操作系统细节的平台适配层(PAL)进行编译。然后,在PAL的基础上进行生成C#编译器等工具。最后,基于上述工具(C#编译器)和PAL编译剩下的部分。
表 1-1 列举了SSCLI源代码的相关目录,本书的随书CD中包含了所有这些代码(也可以从http://msdn.microsoft.com/net/sscli下载)。
· CLI执行引擎
· 对执行引擎的扩展和增强的组件框架
· Tools、测试代码和工具、各种编译器、文档、utilities
1.2.1.1 CLI执行引擎
执行引擎是CLI的心脏。它包含组件模型和一组运行时服务,如异常处理、自动化的堆栈管理等。在许多方面,它就像一个大kahuna。执行引擎中包含了我们提到过的“运行时”或“虚拟执行环境”的代码。JIT编译、内存管理、程序集和类的加载、类型解析、元数据解析、堆栈行走和其他的基本机制都是在执行引擎中实现的。可以在sscli/clr/src目录和vm、fjit、md、fusion中找打执行引擎的大部分代码。
图 1-4 众多库合在一起才能运行托管代码
如图1-4中的所示,执行引擎由一系列的动态链接库组成而不是单个的可执行体。clix程序启动器(或者任何希望使用执行引擎的程序)加载主共享库:sscoree在进程中创建CLI实例并提供一个启动程序集供执行。执行引擎中并没有主模块,它们是用来托管其他程序。执行引擎依赖于许多其它的共享库,为了保证各部分的独立性它们都被打乱了。比如mscorsn中的加密代码对加载和签名程序是必需的。还有一些可能在其它地方非常有用,比如rotor_pal 和rotor_palrt 被在PAL用到。最后,有些不是一定会用到的代码也被打包进来了,比如为调试提供支持的mscordbc。
1.2.1.2 CLI中的库
SSCLI中不仅仅只包含规范所规定的,比如元数据、通用中间语言和通用类型系统等底层功能,还提供了一些面向生产的高级功能的库。表1-2按照功能列出了这些库。
这些库提供了一些使用底层操作系统相关功能的接口,在某种程度上它们是为CLI专门提供的,目的是提供程序员的生产力以及产品质量。
这些API还有一个不太明显的作用:它们展示使用集成组件进行开发的便捷性,使用它们可以更容易的生产出更好的组件。而服务,最大程度的减少了组件开发者需要实现的功能;或者说最大程度的降低了管理各种组件的复杂度,使整合组件更加方便安全(也减少了代码量)。当组件越少依赖于其它组件提供的功能,或者当组件需要为其他组件提供功能越少时,程序的bug就会越少。为了实现前面所说的各种好处,组件需要在一个专门设计的环境中以托管的方式执行。
有人可能会想CLI库就像一个更先进的C运行时库。不过CLI并没有试图提供可供所有程序员所需要的一切功能,反而只是提供了一组几乎是对所有程序员都有用的组件。sscli/clr/src/bcl中的基础库作为CLI规范的一部分所有的CLI都必须实现,它们是实现可移植这个目标的基础。另外一些库,位于sscli/fx,、sscli/clr/src/classlibnative,和 sscli/managedlibraries中,是可选的标准库或者只是SSCLI的实现。微软发布的商业版本的.NET框架囊括了SSCLI中的所有库。
|
1.2.1.3 平台适配层
咋看之下PAL是一个非常有趣的部分。当然,就和任何其它适配或驱动层一样,就是一堆为了让程序能在所有操作系统上运行的代码。PAL的首要目标是对上层隐藏各种操作系统的实现细节。对SSCLI而言这个选择就显而易见了:特定于Win32 API,SSCLI的PAL被设计成Win32 API的子集(参见sscli/pal/rotor_pal.h)。也就是说并不是完全实现Win32 API,因为它只需要提供CLI指定的调用接口。不要将PAL层看作一个通用的Win32模拟层,因为它是不完整的。
PAL层使得Rotor具有跨平台的能力,因为用来构建Rotor的工具和资源都是基于PAL层的。有关的资源可以在sscli/pal/unix目录中找到。PAL层还做了大量工作来提供通用的异常处理、线程、IO、同步、调试以及更多其它特性。某些宿主进程,比如Web服务器和数据库,很可能有其自己的运行时机制,这也是PAL需要考虑的因素。由于这个原因以及PAL的定义是如何使用操作系统及相关资源,所以理解PAL的实现非常重要。
除了PAL,在sscli/palrt/src文件夹中有一个实现了SSCLI所需的Win32 API的库,但并不依赖于操作系统的实现。这个库也包含一组特定于PAL的API。比如小数运算,一个COM组件模型的stub实现、数组处理、内存管理以及许多其它的工具函数。
PAAL最有趣的方面是与执行引擎控制方面有关。SSCLI被设计成可以和本地代码在本地进程中同时运行,这就意味着许多操作系统的系统调用需要被执行引擎捕获以便获得运行时所需的相关信息,比如垃圾回收或安全控制系统。PAL层的关键作用是:SSCLI的实现是基于PAL所提供的抽象,没有它就不可能保证代码隔离、安全检查和运行时控制。例如,线程和异常处理都是在PAL中实现的,它们对执行引擎的运行而言都是至关重要的,因为后者使用异常帧来跟踪托管代码,并使用线程相关的堆栈来存放各种数据。PAL方面的详情将在第六章中讨论,第九章将讨论PAL的设计。
1.1.2.4 Tools、各种编译器、测试代码和工具、文档和Utilities
Rotor中有相当比例用以进行构建、测试和用以实现CLI的支持代码。比如前面讨论过的PAL就是如此。可以在Rotor中找到有大量的开发工具、utilities和测试代码。一类是是用来做开发管理的工具,还有一类是用来进行构建的。
就开发管理而言,Rotor中的许多工具都是Microsoft .NET框架平台的开发人员的所熟悉的,因为两者都是用了一些共享的utilities,比如链接器、汇编器以及反汇编器。sscli/clr/src, sscli/clr/src/tools以及sscli/clr/src/toolbox目录包含了这些utilities,以及SSCLI中为开发和运行所提供的特有代码,比如clix.exe。程序员可以从sscli/docs找出哪些功能是Rotor和.NET框架所共有的,哪些不是通用的。
用来构建Rotor的工具可以再sscli/tools中找到。这些工具基于PAL构建并用以跟踪各种依赖关系、启动构建进程、组装库和可执行体,最后将构建结果放入sscli/build目录。Rotor各组件之间的关系是错综复杂的,它们彼此相互关联,因此这些工具非常重要。
一旦构建SSCLI成功,能够使用sscli/tests目录中的代码对其进行测试。特别值得注意的是对PAL的测试,可以在sscli/tests/palsuit中找到。可以用来验证PAL的实现是否正确,或者修改PAL后验证修改是否正确。sscli/tests/bvt目录中的开发人员构建验证测试(BVT)可以用来验证执行引擎是否构建完成。也有一些其它方面的测试,比如针对基础类库的测试。大部分测试和BVTs及测试工具一起都在sscli/tests/harness目录中,相关文档则可参看sscli/docs/testing_overview.html。
Rotor的文档和技术说明都放在sscli/docs目录中。对于浏览和修改代码、理解CLI架构及SSCLI在具体实现原理都是非常有用的参考材料。也有一份关于PAL的详细规格说明书,这对于移植Rotor非常有用。花一些时间看看这些文档还是非常值得的。
1.2.2 本书的范围
本书着眼于如何实现CLI组件模型及其底层执行引擎在SSCLI中的实现。由此我们将对操作系统原理、通用可移植方面的问题进行一些简短的讨论。至于对于编译器、语言和框架将不作讨论。而CLI非面向组件方面的用途也不会被讨论,因为我们可以找的许多.NET框架和CLI方面的书。
在此非正式的声明:本书中采用的众多来自SSCLI源码的C++代码基本都被整理过,换成了伪代码。一些的宏被删掉了,并在真是代码中加入了错误处理以及一些断言,代码更具有可读性。如果你计划在SSCLI添加代码或对其进行修改,应该和SSCLI保持一致的编码规范和错误处理方式。附录D在这方面进行了一些简短的描述。
1.2.3 总结
CLI是第一个在底层设计上就考虑了多语言的虚拟执行环境。平台提供者、框架构建者以及程序员不再需要为采用何种语言纠结了。反而会从CLI获得异常处理、垃圾回收、反射、代码安全以及可扩展的数据驱动元数据架构。由于增强的互操作和共享基础架构,使用CLI能够很容易在已有代码的基础上进行组件式开发。
CLI的标准格式使得打包和部署CLI组件既不依赖于特定操作系统也不依赖于特定编程语言。这个非常重要,因为CLI的数据驱动元数据架构是基于这种格式的。数据驱动的元数据提高了程序员的生产效率,它使得各种技术、库和工具可以无缝的集成。数据驱动的元数据也保证了组件可以永不过时。
抽象的指令集和类型系统为CLI带来了一个非常诱人的特性:软件可以随处运行。CLI的设计者当然希望有很多个CLI实现可以在多个不同的平台上运行。不过现实情况是每个CLI实现都提供乐于提供一些特有的框架、服务、工具或者其它的语言特性。这就导致现实中的CLI和C语言很类似,很少有只使用了标准库所提供的功能的应用,而是既使用标准库也使用一些平台特定或者可以跨平台的非标准库。类似的,大多数CLI程序也将既使用标准组件也使用一些平台特定或者跨平台的第三方组件。
CLI的语言无关特性、数据驱动元数据架构以及虚拟执行模型提供了一个可供组件之间有效的合作而不用牺牲安全性和自主性的舞台。元数据为可供扩展组件行为和注入安全特性的提供了基础。CLI执行模型中的每个步骤都会从上一步骤获取相应数据,进行转换或者添加一些新数据,然后传给下一步骤。本书描述了执行引擎执行过程中的所有步骤,从最初的引导开始直到最后一个托管资源的销毁。
转载于:https://blog.51cto.com/kingwufan/532110