CLR笔记:1.CLR的执行模型
术语: CLR :Common Language Runtime 公共语言运行期,有多种不同编程语言使用的运行库
托管模块:Managed Module,一个标准的MS Window可移植执行体文件(32位PE32或64位PE32+)
IL:Intermediate Language 中间语言,又叫托管代码(由CLR管理它的执行)
元数据:metadata,一系列特殊的数据表
程序集:Assembly,抽象的
JIT:just-in-time 即时编译,将IL编译成本地CPU指令(本地代码)
FCL:Framework Class Library,Framework 类库
CTS:Common Type System,通用类型系统,描述了类型的定义及其行为方式
CLI:Common Language Infrastructure,公共语言基础结构,这是MS提交给ECMA的一个标准,由CTS和其他Framwork组件构成
CLS:Common Language Specfication,公共语言规范,详细规定了一个最小特性集
1.1 将源代码编译成托管模块
CLR编译过程: C#源码文件——C#编译器编译——托管模块(IL和元数据)
托管模块的各个部分:
1.PE32或PE32+头
标志了文件类型,GUI/CUI/DLL,文件生成时间,在32位还是64位上运行
2.CLR头
CLR版本,入口方法,模块元数据,资源,强名称
3.元数据
3种类型的表
4.IL代码
元数据包括:
1.描述了模块中定义的内容,比如类及其成员
2.指出了托管模块引用的内容,比如导入的类及其成员
3.清单manifest,描述了构成Assembly的文件,由Assembly中的文件实现的公共导出类型,与Assembly相关联的资源/数据文件
元数据总是嵌入到与代码相同的EXE/DLL中,始终与IL保持同步。
元数据用途:
1.消除了对头/库文件的依赖,直接从托管模块中读取
2.智能感知,从元数据中解析
3.代码验证,使用元数据确保代码只执行安全操作
4.正反序列化
5.垃圾收集器跟踪对象的生存期以及对象的类型
1.2 将托管模块合并成程序集
程序集:一个或多个托管模块/资源文件的逻辑分组,是最小的重用,安全性以及版本控制单元。
既可以生成单文件程序集,也可以生成多文件程序集,这由编译器工具决定。
CLR是和程序集一起工作的,而不是和托管模块
1.3 加载CLR
CLRVer命令,查看机器上所有CLR版本
csc的 /plattform开关,决定生成什么样的程序集:AnyCPU,x86,x64,Itanium
1.4 执行Assembly代码
ILAsm命令,将IL编译成Assembly;ILDasm将Assembly编译成IL。
高级语言(C#)只是CLR的一个子集,IL则允许访问CLR的所有功能。
JITCompiler函数,又名JIT编译器(JITter)
在方法首次执行时,CLR检测出Main的代码引用的所有类型,于是CLR分配一个内部数据结构,用于管理对引用类型的访问。
在这个内部结构中,每个方法都有一条对应的纪录以及地址。
对此结构进行初始化时,CLR将每条纪录都设置为CLR内部包含的一个未文档化的函数,即 JITCompiler函数。
JITCompiler函数被调用时,找到相应方法的IL,编译成本地CPU指令,并保存到一个动态内存块中,将该内存地址存入内部结构中,最后JITCompiler函数会跳转到内存块中的代码,执行。
第二次执行该方法时,不需要再编译,直接执行内存块中的代码。
JIT将本地代码保存在动态内存中,一旦程序终止,本地代码会被丢弃。
csc命令有2个开关会影响代码的优化:/optimize ,/debug
开关设置 | IL代码质量 | JIT本地代码质量 | |
/optimize- ,/debug- | 未优化 | 优化 | 默认设置 |
/optimize- ,/debug(+/full/pdbonly) | 未优化 | 未优化 | VS2005 Degug状态 |
/optimize+ ,/debug(-/full/pdbonly) | 优化 | 优化 | VS2005 Release状态 |
生成未优化的IL时,会在IL中生成NOP指令用于调试,设置断点。
IL是基于堆栈的。所有指令都是:将操作数压栈,结果则从栈中弹出
IL有安全验证机制,保证每一行IL代码是正确的,不会非法访问内存,每个托管EXE都在独自的AppDomain中运行。
不安全代码:允许C#直接操作内存字节,在COM互操作时使用,csc以/unsafe开关标记包含不安全代码,其中所有方法都使用unsafe关键字。
PEVerify命令检查程序集所有方法,指出其中的不安全代码方法。
1.5 本地代码生成器 NGEN.exe
NGEN.exe将IL预先编译到硬盘文件中,可以加快程序的启动速度,减小程序的工作集(所有加载该程序集的AppDomain不再copy其副本,因为该程序集已经与编译到文件中,是代码共享的)。
缺点是:
不能保护IL外泄
生成的文件可能失去同步
因为在文件中要计算首选基地址,而NGEN是静态计算好的,所以要修改基地址,速度会慢下来
较差的执行性能,NGEN生成的代码没有JIT好。
如果不能使用NGEN生成的文件,会自动加载JITCompiler。
1.7 CTS
CTS的一些规定:
1.一个类型可以包含0个或多个成员
2.类型可视化以及类型成员的访问规则
3.定义了继承,虚方法,对象生成期的管理规则
4.所有类型最终都从预定义的System.Object继承
1.8 CLS
如果在C#中定义的类型及其方法,可以在VB中使用,那么,就不能在C#中定义CLS外的任何public/protected特性,privated的类型及其成员不受限制。
C#可以有仅大小写不同的两个方法——不符合CLS,所以不能是public的。
使用[assembly:CLSComplant(true)]标志程序集,告诉编译器检查该程序集的CLS相容性。书上写得不明白,我这里做了一个测试:
[assembly: CLSCompliant(true)]
namespace ClassLibrary2
{
public class Class1
{
public void A()
{
}
public void a()
{
}
}
}
注意,[assembly:CLSComplant(true)]要写在namespace外。
我定义了两个不同方法A和a,编译器会有警告,说这样的语法不兼容CLS;如果去掉[assembly:CLSComplant(true)]声明,那么不会有这个警告;如果将a方法改为private,则不会有警告。
中途我使用了ILDasm观察这个dll,发现两个方法A和a都存在于IL中,说明IL的语法范围也大于CLS。
在VB中,我添加了对此dll的引用:
Module Module1
Public Class T
Public Function A() As Integer
Dim c1 As Class1 = New Class1()
End Function
End Class
End Module
发现,在c1.后面不会有A或a方法的智能感知,说明VB不能识别不符合CLS的语法。如果修改了dll中的a方法为private或者删除a方法,则在VB中可以智能感知到A方法。
可以得出结论,不符合CLS的语法,在另一种语言中是看不到的。
1.9 COM互操作
3种互操作情形:
1.托管代码可以调用DLL中包含的非托管函数,如Kernal32.dll,User32.dll
2.托管代码可以使用现成的COM组件
3.非托管代码可以使用托管类型(C#写的ActiveX控件或shell扩展)