通过MSIL了解CLR的运行原理
作者: Wednesday, April 17 2002 2:12 PM
作为.NET最低层次的公共基础,微软中介语言(MSIL或IL)对一般开发者具有非常重要的意义。除了好奇心以外,仔细研究应用程序的IL能让你更为清楚地了解到公共语言运行时(CLR)执行高级C#或VB.NET代码的基本原理,从而有助于你发现和解决一些比较细微的问题。
在这篇文章里,我将引领读者了解IL,学习有关的一些关键指令,同时对CLR的操作机理做一点基础性解释。我不打算教你用IL编程,而是分析一些IL语法和语句使你对IL有更多了解。
ILDASM简介
微软的IL拆卸实用程序Ildasm.exe(通常位于\Program Files\Microsoft.Net\FrameworkSDK\Bin目录下)可以析构.NET assembly(装配)、根据你的要求从程序中抽取IL代码。对某一assembly调用该使用程序后,ILDASM会给出该assembly中所有类和名称空间的一个视图,如图A所示:
" target=_blank>http://www.zdnet.com.cn/i/developer/story/39032789/image001.gif
图A
ILDASM浏览assembly
当你进到某个类的成员或其方法,ILDASM就会为你显示该成员的IL代码。如果之前你曾经看到过汇编器或J++字节码,那么IL可能在你看来会觉得有点眼熟。在另一方面,如果你仅对抽象的高级程序语言有所了解,那么IL看起来更像是胡言乱语。
好,现在你知道如何窥视assembly的IL代码了,但这些代码都意味着什么呢?在回答这个问题之前,首先让我们先来了解下CLR的有关知识。
虚拟CPU
对.NET程序来说,.NET CLR在功能上就如同一块虚拟的CPU,它执行IL代码、操作数据。CLR和真实的CPU类似之处在于它们都不直接操作内存中的变量而是使用程序变量的临时拷贝,CLR把这些程序变量存放在堆栈上。从内存拷贝某个变量到堆栈的行为称做装载(loading),而从堆栈拷回某个变量到内存的行为则被称做存储(storing)。
所以把两个数字相加的过程应该是这样的:
1.装载第1个数字并把它推入堆栈。
2.装载第2个数字并把它推入堆栈。
3.从堆栈中取出这两个数字并把它们相加。
4.把结果存储到内存。
什么是堆栈?
理解IL的关键是知道堆栈的工作原理。堆栈是一种抽象数据结构,其操作机理是后进先出。当你把新条目推进堆栈时,已经在堆栈内的任何条目都会压到堆栈的深处。同样的,把一个条目从堆栈移出则会让堆栈内的其他条目都向堆栈的顶部移动。只有堆栈最顶端的条目能从堆栈中取出,条目离开堆栈的顺序和它们被推进堆栈的顺序一样。你不妨回想下自动售货机的装货和取货过程就明白了。
重要的IL语句
既然你已经明白了CLR操作的基础知识,下面我们就接着讨论你面前的那些代码。怎么?没有看到什么代码?那么请你看看这里列出的IL代码。
Sidebar A: Sample IL listing
.method private instance void Listen() cil managed
{
// Code size 169 (0xa9)
.maxstack 4
.locals init ([0] unsigned int8[] buff,
[1] class [System]System.Net.Sockets.NetworkStream ChatStream,
[2] class [System]System.Net.Sockets.TcpClient RemoteClient,
[3] string strHandle,
[4] class Chat.ChatServer/TextRelayer 'handler',
[5] class [mscorlib]System.Threading.Thread t)
IL_0000: nop
IL_0001: nop
.try
{
IL_0002: br IL_0091
IL_0007: ldarg.0
IL_0008: ldfld class [System]System.Net.Sockets.TcpListener Chat.ChatServer::Listener
IL_000d: callvirt instance class [System]System.Net.Sockets.TcpClient [System]System.Net.Sockets.TcpListener::AcceptTcpClient()
IL_0012: stloc.2
IL_0013: ldloc.2
IL_0014: callvirt instance class [System]System.Net.Sockets.NetworkStream [System]System.Net.Sockets.TcpClient::GetStream()
IL_0019: stloc.1
IL_001a: ldc.i4.s 26
IL_001c: newarr [mscorlib]System.Byte
IL_0021: stloc.0
IL_0022: ldloc.1
IL_0023: ldloc.0
IL_0024: ldc.i4.0
IL_0025: ldc.i4.s 24
IL_0027: callvirt instance int32 [System]System.Net.Sockets.NetworkStream::Read(unsigned int8[],
&n