读书笔记(2)"/>
《CLR via C#》读书笔记(2)
- 平台确定
通过第一节,我们知道:编译器在PE文件中写入了大量的元数据。其中在PE32(+)中有一个machine字段用来标识该程序集所面向的平台(一般应该是x86,x64,不确定(Any CPU)).
在当前运行平台与程序集面向平台兼容的情况下,程序集为以所指定的平台类型运行(比如:面向x86则分配32从位地址空间,为其加载x86版本的所引用系统程序集;面向x64则为其分配64位的地址空间,为其加载x64版本的所引用系统程序集;如果any cpu则根据当前运行平台确定。)
- 实例解剖CLR运行模型
以如下代码为例: class Program
{
static void Main( string[] args)
{
Console.WriteLine( " Hello World ");
}
}
生成如下IL代码: .method /* 06000001 */ private hidebysig static
void Main( string[] args) cil managed
// SIG: 00 01 01 1D 0E
{
.entrypoint
// Method begins at RVA 0x2050
// Code size 13 (0xd)
.maxstack 8
IL_0000: /* 00 | */ nop
IL_0001: /* 72 | (70)000001 */ ldstr " Hello World " /* 70000001 */
IL_0006: /* 28 | (0A)000011 */ call void [mscorlib /* 23000001 */]System.Console /* 01000013 */::WriteLine( string) /* 0A000011 */
IL_000b: /* 00 | */ nop
IL_000c: /* 2A | */ ret
} // end of method Program::Main 生成如下CLR Header(部分):
----- CLR Header:
Header size: 0x00000048
Major runtime version: 0x0002
Minor runtime version: 0x0005
0x00002068 [ 0x00000640] address [size] of Metadata Directory:
Flags: 0x00000003
Entry point token: 0x06000001 CLR将这个程序Run起来的步骤如下:
1. 找到入口方法
我们可以看到通过CLR Header的Entry point token字段可以知道,该程序集的入口方法是位于MethodDef表中(token 0x06 代表是methoddef表)的第一条记录所代表的方法。通过查询MethodDef表我们可以找到该方法的IL在该文件中的偏移。CLR会将IL编译为本地CPU指令后再执行该代码。
2. 查找引用类型/成员
其实该代码非常简单,只是调用System.Console.WriteLine方法向输出流上写了一段字符“Hello World”.
从IL指令可以看到,该行代码分为两部分来执行:第一步是将"Hello World"字符串入栈;第二步是调用System.Console类型的WriteLine方法
第一步没什么可说的,主角是第二步。
第二步是调用一个外部引用的类型System.Console的方法WriteLine. CLR对该方法的调用过程其实也是费很大一番功夫,主要包括如下几步:
(1) 加载程序集
这一步其实是在CLR JIT将Main方法编译成本地代码时就进行了的。CLR会查找Main方法中所有的引用的外部类型,并将这些外部类型所在的程序集全部加载进来。
(2) 查找方法IL
对于引用的外部类型方法,CLR查找IL代码步骤如下:
-首先查找MemberRef表,找出定义该方法的类型
-然后查找TypeRef表,找出该类型是在当前程序集还是在别的程序集
--若是当前程序集,则通过ModuleRef能找到其所在Module,通过Module的MethodDef元数据可以知道方法的IL的存放位置偏移
--若不是当前程序集,则通过查找目标程序集的清单元数据查找到目标Module,最后也是通过MethodDef元数据表找到IL的存放位置。
对于当前的例子,CLR首先从MemberRef中找到WriteLine方法对应的条目,然后找到其所在类型System.Console在TypeRef中所对应的条目。
通过该元数据可以知道其属于mscorlib这个程序集,并可以导航至AssemblyRef元数据表。通过AssemblyRef元数据表,我们便可以定位到mscorlib这个程序集
的清单文件,进行最终找到定义System.Console这个类型的Module,从该Module的MethodDef数据表中找到WriteLine方法的IL存放地址。
(3) 构造数据结构保存方法的本地指令地址
因为此处访问了System.Console这个类型的方法。CLR会
首先在内存中为Console这个类型创建一个数据结构(假设称之为ConsoleLinker),这个数据结构是为了保存Console中所有方法的本地代码地址。但是当这个数据结构被初始化的时候,每一个方法的本地代码地址都被设为JIT的JITCompiler方法的地址。
然后,当调用WriteLine方法时JITCompiler函数会被调用,这个函数所做的事情是先将步骤(2)所找到的方法IL代码编译成本地代码,然后保存于内存中,并将
ConsoleLinker中保存WriteLine方法地址的地方设为本地代码的地址。(当以后再次访问这个方法时,便可以直接执行本地代码,而不用经过JIT编译了)
(4) 执行方法调用
通过以上步骤,已经可以知道所引用外部方法的本地代码地址.CLR会直接去执行该地址所保存的本地代码。
最后附上两幅图,我觉得最能代表整个执行过程:
第一幅,CLR查找所引用的类型的模型:
第二幅,当调用引用的类型方法时,其调用模型为:
转载于:.html
更多推荐
《CLR via C#》读书笔记(2)
发布评论