您的位置:首页 > 编程语言 > C#

CLI里面的秘密……总体介绍,以及CIL (转载)

2005-03-25 14:17 253 查看

CLI里面的秘密……(一)总体介绍,以及CIL

不知道大家对于Common Language Infrastructure有什么认识呢?“噢!天啊!看到那么几个英文我就头痛了!”如果真是这样,那么你就没有办法继续看下去了,因为这里面的东西基本上只能够找到英文的资料。
实际上这个看似很深奥的东西并没有你想象的那么难,当然,也不是一个简单的东西。关于这方面的资料其实非常好找,虽然数量不多,但是却是非常之实用,就在你VS安装的目录里面。假如你安装的是VS2003,装在了E:/VS.NET 2003这个目录里面,那么相关的资料就在E:/VS.NET 2003/SDK/v1.1/Tool Developers Guide/里面。这个目录下面有两个子目录,一个是docs,全部都是文档,另外一个是Samples,全部都是例子,不过所有的东西都是英文的。
什么是CLI呢?中文应该翻译成“公共语言底层结构”。CLI应该包括CIL和CLR:CIL是Common Intermediate Language,中文是“公共中间语言”,也就是那个“IL汇编”;CLR是Common Language Runtime,中文是“公共语言运行库”。除了这些之外,任何一个.NET语言还收到CLS的约束,CLS——Common Language Specification,公共语言规格说明书,这个东西主要用于约束所有.NET语言,使得他们能够互相协作,不存在某种语言产生的代码不能够被另外一种语言所支持。
如果你真的有兴趣看看我说的那个目录里面的文档,那么你会发现几乎你所能够想得到的底层的东西都有了,包括CIL的语法和二进制代码,CLI可执行程序文件结构,怎么写一个调试器(Debugger),分析器(Profiler),编译器(Compiler)……什么?你不相信连编译器都有了?在 E:/VS.NET 2003/SDK/v1.1/Tool Developers Guide/Samples里面有三个编译器的文件!一个Lisp.NET,一个MyC,一个Simple Managed C。还有更多的例子呢,诸公自便。
今天首先讲讲CIL以及CIL的VM。关于CIL的文档,在Partition III CIL.doc中。如果不想看的话,我可以简单讲讲。CIL的VM是一个栈式机,和x86的依赖寄存器的机制很不一样,所谓的栈式机就是说指令所需要的数据都用堆栈保存。这种栈式机在真实的计算机当中并不多见,尤其在CISC的CPU里面是很难见到的,即使在RISC都很少见(也许我孤陋寡闻吧)。原因简单点讲就是栈式机的指令非常简单,为了进行一个计算需要多个指令来完成,甚至需要多次读出和写入数据。正是由于指令简单,所以由于虚拟机上面却非常的方便,虚拟机程序做起来可以容易许多。栈式机主要的指令有五大类:(数据)压栈、弹出、运算、转移、其他,其中前三个是栈式机的核心。以CIL为例:
int a = a + b + c + d + e + f + g + h + i;
80x86 ASMCIL
0000004c 8B 54 24 10 mov edx,dword ptr [esp+10h]
00000050 03 54 24 14 add edx,dword ptr [esp+14h]
00000054 03 54 24 18 add edx,dword ptr [esp+18h]
00000058 03 54 24 1C add edx,dword ptr [esp+1Ch]
0000005c 03 54 24 20 add edx,dword ptr [esp+20h]
00000060 03 D5 add edx,ebp
00000062 03 D6 add edx,esi
00000064 03 D3 add edx,ebx
00000066 03 D7 add edx,edi
00000068 89 54 24 10 mov dword ptr [esp+10h],edx
IL_0000: 06 ldloc.0 // a
IL_0001: 07 ldloc.1 // b
IL_0002: 08 ldloc.2 // c
IL_0003: 09 ldloc.3 // d
IL_0004: 11 04 ldloc.s e
IL_0006: 11 05 ldloc.s f
IL_0008: 11 06 ldloc.s g
IL_000A: 11 07 ldloc.s h
IL_000C: 11 08 ldloc.s i
IL_000E: 58 add // stack[top] = stack[top--] + stack[top--] = stack[top] = h + i;
IL_000F: 58 add // stack[top] = g + h + i
IL_0010: 58 add // = f + g + h + i
IL_0011: 58 add // = e + f + g + h + i
IL_0012: 58 add // = d + e + f + g + h + i
IL_0013: 58 add // = c + d + e + f + g + h + i
IL_0014: 58 add // = b + c + d + e + f + g + h + i
IL_0015: 58 add // = a + b + c + d + e + f + g + h + i
IL_0016: 0A stloc.0 // a = ...
表一 我们先看看右边的CIL,ldloc的意思是把局部变量压到堆栈里面,后面的.0表示第零个局部变量(也就是a了);add则把栈顶的两个元素弹出来并且相加,结果压到堆栈顶上;stloc则把栈顶的内容弹出来,保存到局部变量当中。现在应该看出来了,ld就是load的缩写,st是store的缩写,loc是local。那么还有没有其他的呢?大家可以看一下下面的表:
主要操作操作数范围/条件操作数类型操作数
缩写全称含义缩写全称含义缩写全称含义缩写全称含义
ldload将操作数压到堆栈当中,相当于:
push ax
argargument参数??操作数中的数值.0?第零个参数 *
.1?第一个参数
.2? 第二个参数
.3?第三个参数
.s xx(short)参数xx
aaddress操作数的地址只有 .s xx,参见ldarg.s
loclocal局部变量参见ldarg
fldfield字段(类的全局变量)参见ldargxx?xx字段,eg:
ldfld xx
cconst常量.i4int 4 bytesC#里面的int,其他的类型例如short需要通过conv转换.m1minus 1-1
.0?0
.1?1
 ……
.8 8
.s(short)后面跟一个字节以内的整型数值(有符号的)
??后面跟四个字节的整型数值
.i8int 8 bytesC#里面的long??后面跟八个字节的整型数值
.r4real 4 bytesC#里面的float??后面跟四个字节的浮点数值
.r8real 8 bytesC#里面的double??后面跟八个字节的浮点数值
nullnull空值(也就是0)??????
ststore将堆栈内容弹出到操作数中,相当于:
pop ax
参见ld **
convconvert数值类型转换,仅仅用纯粹的数值类型间的转换,例如int/float等???.i1int 1 bytesC#里面的sbyte???
.i2int 2 bytesC#里面的short
.i4int 4 bytesC#里面的int
.i8int 8 bytesC#里面的long
.r4real 4 bytesC#里面的float
.r8real 8 bytesC#里面的double
.u4uint 4 bytesC#里面的uint
.u8uint 8 bytesC#里面的ulong
b/brbranch条件和无条件跳转,相当于:
jmp/jxx label_jump
br??无条件跳转?????后面跟四个字节的偏移量(有符号)
.s(short)后面跟一个字节的偏移量(有符号)
falsefalse值为零的时候跳转???参见br
truetrue值不为零的时候跳转???
beqequal to相等???
nenot equal to不相等ununsigned or unordered无氟好的(对于整数)或者无序的(对于浮点)
gtgreater than大于
ltless than小于
gegreater than or equal to大于等于
leless than or equal to小于等于
callcall调用?????(非虚函数)?
???virtvirtual虚函数
* 最左边的是参数0,然后是参数1、2、3……。如果不是在静态当中,参数0相当于C#的this(VB的Me),该参数不需要代码传递,此时最左边的参数是参数1,也就是从.1开始。
** starg只有.s形式,没有.0、.1等形式,除此之外和ldarg相同。
+ 从左到右依次合并,就可以得到一个指令,例如:ld + arg + a + .s xx? =>? ldarga.s xx,即,读出参数xx的地址并压栈。上面这些只是一些比较常用的、但是稍微难理解的指令,其他的可以看Partition III CIL.doc这个文档。现在我们再回到表一的例子,是不是觉得其实CIL非常好理解呢?所以虚拟机用栈式机的形式是比较容易实现的,比起x86里面一堆的寄存器、状态/标志位以及指令对状态/标志位的影响,可简单多了!不过我们也可以看到,用80x86汇编10条指令能够完成的操作,用CIL则需要18条指令,而且这已经是经过手动优化过的CIL了,如果你用C#写,然后编译出来的很可能还要多出一些代码。如果我们数一下包含的字节数,可以看到80x86有32bytes,而CIL只有20bytes,也许会让你觉得CIL似乎更为紧凑,其实是因为这个CIL是一个优化形式,实际的情况CIL并不会比x86汇编小多少,甚至完全可能更大!栈式机另外一个问题是,每一次的操作都必须访问至少两次内存,并且这两次访问的肯定不是同一个地方:一个是某一个内存块,另外一个是堆栈。因此不可能象x86CPU那样,直接访问CPU内部存储器,甚至连访问缓存效率都会打对折(需要访问两个完全无关的地方)。而我们知道CPU内部存储器式最快的,完全没有延时,缓存次之(一级缓存延时约1到2个周期,二级缓存延时3到5个周期),最慢的就是内存了(延时约十个周期左右,甚至更长)。所以一般说来,真实的CPU是很少做成栈式机形式的。
那么我们阅读cil有些什么技巧呢?我觉得需要注意这么几点:
牢记这是一个栈式机,所有指令都和栈有关。
注意当前函数是否为静态函数
就这么多了
注:原文出自http://dotnet.mblogger.cn/sumtec/posts/193.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐