一、将源代码编译成托管代码
.NET Framework作为一种开发平台,可面向多种不同的编程语言,因此生成不同类型【Windows桌面应用程序,Windows Store应用程序,Web应用程序,WCF服务】的应用程序或组件。
CLR,公共语言运行时,就是一个可由多种编程语言使用的“运行时”。
CLR的核心功能有:内存管理、程序集加载、安全性、异常处理和线程同步。这些核心功能可由面向CLR的所有语言使用
面向CLR的任何语言通过对应的编译器【“正确语法检查”,“正确代码分析”】进行编译都会生成托管模块【中间语言和元数据】。托管模块是标准的32位Microsoft Windows可移植执行体文件,或是标准的的64位Windows可移植执行体(PE32+)文件,它们都需要CLR才能执行。每个面向CLR的编译器生成的都是IL代码【中间语言】,IL代码有时称为托管代码,因为CLR管理它的执行,换言之,由CLR管理的代码可称为托管代码。
编译器同时生成元数据和代码,把它们绑定在一起,并嵌入最终生成的托管模块,所以元数据和它描述的的IL永远不会失去同步。
1.1 托管模块的各个组成部分
1.2 元数据的作用
概述:元数据,数据表集合,一些数据描述了模块中定义了哪些类型和成员,另一些数据描述了引用哪些类型和成员。元数据 是一种二进制信息,用以对存储在公共语言运行库可移植执行体文件 (PE) 文件或存储在内存中的程序进行描述。
描述:元数据以非特定语言的方式描述在代码中定义的每一类型和成员。元数据存储以下信息:
-
程序集的说明。
-
标识(名称、版本、区域性、公钥)
-
导出的类型。
-
该程序集所依赖的其他程序集。
-
运行所需的安全权限。
-
-
类型的说明。
-
名称、可见性、基类和实现的接口。
-
成员(方法、字段、属性、事件、嵌套的类型)。
-
-
属性。
-
修饰类型和成员的其他说明性元素。
-
作用:
由于在IL代码中已经包含了关于引用类型的所有成员和类型的信息,且编译器直接从托管模块中读取元数据,因此避免了编译时对原生C/C++头和库文件的需求;
VS通过元数据实现智能感应;
CLR的代码验证过程使用元数据确保代码只执行“类型安全”的操作;
元数据允许将对象的字段序列化到内存块,将其发送给另一台机器,然后反序列化,在远程机器上重建对象状态;
元数据允许垃圾回收器跟踪对象生存周期;
二、将托管代码合并成程序集
程序集是一个或多个类型定义文件及资源文件的集合。在程序集的所有文件中,有一个文件容纳了清单。清单也是一个元数据表集合,表中主要包含作为程序集组成部分的那些文件的名称。此外,还描述了程序集的版本、语言文化、发布者、公开导出的类型以及构成程序集的所有文件。
CLR操作的是程序集。换言之,CLR总是首先加载“清单”元数据表的文件,再根据“清单”来获取程序集中的其他文件的名称。
2.1 程序集
程序集是一个或多个模块/资源文件的逻辑性分组;
程序集是重用、安全性以及版本控制的最小单元;
程序集可以是单文件程序集也可是多文件程序集,这取决于选择的编译器或工具;
程序集相当于“组件”;
与非托管组件相比,程序集更易部署;
2.2 托管模块--编译器--程序集
编译器默认将生成的托管模块转换成程序集,即C#编译器生成的是含有清单【PE32(+)文件包含一个名为清单的数据块。清单也是源数据表的集合】的托管模块。
对于一个可重用的、可保护的、版本可控的组件,程序集把它的逻辑表示和物理表示区分开。
在程序集的模块中,包含与引用的程序集有关的信息。这些信息可使程序集自描述,即CLR为了能够执行程序集中的代码,程序集的直接依赖对象是什么。
2.3 将模块合并成程序集
三、加载公共语言运行时--目标机器必须安装好.NET Framework
程序集既可是可执行应用程序,也可以是由可执行程序使用的DLL。
为C#编译器指定不同的/plateform命令开关将得到不同的托管模块。
四、执行程序集的代码
IL,可视为一种面向对象的机器语言,是与CPU无关的语言,由编译器编译而成。为了执行方法,首先必须通过CLR的JIT编译器将方法的IL转换成本机(native)CPU指令。
如下所示:
当第二次调用时:
方法仅在首次调用时才会有一些性能损失。以后对该方法的所有调用都以本机代码的形式全速运行,无需重新验证IL并把它编译成本机代码。JIT编译器将本机CPU指令存储到动态内存中。这意味着一旦应用程序终止,编译好的代码也会被丢弃。所以,当我们再次运行应用程序时,JIT将重新编译。
五、IL和验证
IL基于栈。这意味着它的所有指令都要讲操作数压入(push)一个执行栈,并从栈弹出(pop)结果。
IL指令是“无类型”的。
IL最大的优势不是它对底层CPU的抽象,而是应用程序的健壮性和安全性。讲IL编译成本机CPU指令时,CLR执行一个名为验证的过程。这个过程回检查高级IL代码,确定代码所做的一切都是安全的。
Windows的每个进程都有自己的虚拟地址空间。讲每一个Windows进程都放到独立的地址空间,将获得健壮性与稳定性:一个进程干扰不到另一个进程。