一、CLR的执行模块
1.1 将源代码编译成托管模块
非托管C/C++可对系统进行低级控制,按自己的想法管理内存、VB可以快速生成UI应用程序,并控制COM对象和数据库。
公共语言运行时(Common Language Runtime, CLR)是一个由多种编程语言使用的“运行时”。CLR的核心功能(如内存管理、程序集加载、安全性、异常处理和线程同步)可由面向CLR的所有语言使用。
事实上在运行时,CLR根本不关心开发人员用哪一种语言写源代码,这意味着在选择编程语言时,应选择最容易表达自己意图的语言。
面向CLR的语言编译器:C++/CLI、C#、VB、F#、Iron Python、Iron Ruby以及一个“中间语言”(Intermediate Language, IL)汇编器。除了Microsoft,另一些公司、大学和学院也创建了自己的编译器,也能向CLR生成代码。例如:Ada, APL, Caml, COBOL, Eiffel, Forth, Fortran, Haskell, Lexico, Lisp, LOGO, Lua, Mercury, ML, Mondrian, Oberon, Pascal, Perl, PHP, Prolog, RPG, Scheme, Smalltalk, Tcl/Tk
无论选择哪个编译器,结果都是托管模块(Managed Module)。它们都需要CLR才能执行。
表1 托管模块各个部分
组成部分 | 说明 |
---|---|
PE32或PE32+头 | 标识了文件类型,包括GUI、CUI或者DLL;包含时间标记,如果包含本机CPU代码的模块,会包含本机CPU代码有关的信息 |
CLR头 | 头中包含要求的CLR版本,一些标志(Flag),托管模块入口方法的MethodDef元数据Token以及模块的元数据、资源、强名称、一些标识以及其他不太重要的数据项的位置及大小 |
元数据 | 主要有两种表:描述源代码中定义的类型和成员,描述源代码引用的类型和成员 |
IL代码 | 编译器编译源代码时生成的代码,在运行时,CLR将IL编译成本机的CPU指令 |
IL代码有时被称为“托管代码(Maneged Code)”,因为CLR管理它的执行。
1.1-1 元数据:
面向CLR的编译器要在每个托管模块中生成完整的元数据(metadata)。一些数据表描述了模块中定义了什么(比如类型以及其成员),另一些描述了模块引用了什么(比如导入的类型及其成员)元数据是一些老技术的超集,这些老技术包括COM的“类型库”(Type Library)和“接口定义语言”(Interface Definition Language, IDL)文件,但CLR元数据远比他们全面。
元数据有多种用途,下面仅举例一部分
- 避免了编译时对原生C/C++头和库文件的需求,编译器直接从托管模块读取元数据。
- “智能感知”(IntelliSense)技术会解析元数据,告诉你一个类型提供了哪些方法、属性、事件和字段、乃至方法需要的参数。
- CLR的代码验证过程使用元数据确保代码只执行“类型安全”的操作。
- 元数据允许将对象的字段序列化到内存块,将其发送给另一台机器,然后反序列化,在远程机器上重建对象状态。
- 元数据允许垃圾回收器跟踪对象生存期。垃圾回收器能够判断任何对象的类型,并从源数据知道那个对象中的那些字段引用了其他对象。
为了执行包含托管代码以及/或者托管数据的模块,最终用户必须在自己的计算机上安装好CLR(目前作为.NET Framework的一部分提供)。这类似于为了运行MFC或者VB6.0应用程序,用户必须安装MFC库或者VB DLL。
1.1-2 C++/CLI:
Microsoft C++编译器默认生成包含非托管代码的EXE/DLL模块,并在运行时操纵非托管数据(native内存)。这些模块不需要CLR即可执行。通过指定/CLR命令行开关,C++编译器就能生成包含托管代码的模块。在前面提到的所有Microsoft编译器中,C++编译器是独一无二的,只有它才允许开发人员同时写非托管代码和托管代码,并生成到同一个模块中。他也是唯一允许开发人员在源代码中同时定义托管和非托管数据类型的Microsoft编译器。
1.2 将托管模块合并成程序集
CLR实际不和模块工作,他和程序集工作。程序集(Assembly)是一个抽象概念。首先程序集是一个或多个模块/资源文件的逻辑性分组。其次,程序集是重用、安全性以及版本控制的最小单元。取决于你所选择的编译器或工具,即可生成单文件程序集也可生成多文件程序集。在CLR的世界中,程序集相当于“组件”。
图1有助于理解程序集,图中一些托管模块和资源(或数据)文件准备交由一个工具处理。工具生成