zoukankan      html  css  js  c++  java
  • 工欲善其事

    工欲善其事,必先利其器

                      --论语

    (本文是我正在写的开放电子书《.Net面面观》第二章第一小节,您可以这里和这里查看第一章第二章第二小节

    在开发软件的时候都知道使用Visual Studio提高开发效率,而不是用命令行+NotePad(如果你是在学习那就另当别论了)。那么对于研究.Net一些底层东西,有哪些器呢?

    我认为有三样

    1、 ILDasm与MSIL

    ILDasm,顾名思义,就是IL(MSIL,微软中间语言)的反编译器。各位看官应该都了解下面这张图

    clip_image002

    通过为不同的语言实现编译器,而这些编译器的目标代码都是MSIL(当然,编译器并不只生成MSIL,至于其余的东西,后面再做介绍)。

    那么MSIL又是怎么样的一门语言呢?我在这里无意介绍MSIL的详细语法(关于是否应该学习IL的争论在博客园里已经讨论了两次了,在这里我不想再次引起纷争)。我也不推荐大家花费很多时间去学习MSIL的细节内容,除非你要实现一门在.NET平台运行的语言,如果你只想像我一样,了解一些语法糖或编译器在背后干的事儿,那你只需要阅读下面的几百字就可以了。

    Start with ILDasm

    开始菜单,程序->Visual Studio(不管哪个版本)->Visual Studio Tools->肯定有一个命令符的快捷方式,打开这个后在提示符后输入:ILDasm,就会启动ILDasm程序了。

    clip_image004

    上图是用ILDasm打开一个托管程序集后的图示,ILDasm会用不同的图标表示程序中不同的元素。用ILDasm我们不仅仅可以看到编译器生成的MSIL代码,还可以看到生成的元数据。双击一个节点可以弹出一个显示代码的窗口:

    clip_image006

    哦,你说不懂IL,但是你只要不深究上面这几行代码“所有”的意思,你应该能大致明白它是干啥的:定义一个类,该类继承自System.Object。不难吧。你会发现在这些代码中还穿插着很多前面带点的东西,比如这里的.class,这就是元数据(什么是元数据?哦,简单点说就是编译器除了IL代码外,还附赠一些更多信息,让CLR运行这些程序集时能对它了解的更多)。还有这里的public,auto,ansi,beforefieldinit,这些都是元数据,这些举足轻重的元数据,作用大的可以影响到CLR运行代码的方式。对于元数据的作用超出了本节的范围,在以后的章节中会涉及。

    MSIL是基于栈的语言

    MSIL的核心就是一个运算栈,提到栈,大家都知道FILO(First In,Last Out,先进后出)这个特性。在MSIL中,所有的方法、操作数的参数都来自于这个栈上,比如一个Add方法需要两个参数,那么栈顶的两个元素就会弹出。而Add运算完成后,会返回一个值,这个值又将被压到这个栈顶。调用这个Add方法的代码类似下面这样:

    ldc.i4.5

    ldc.i4.8

    callvirt Yuyijq.StudyIL.Add(int,int)

    哦?不懂那些命令是啥意思?没关系,猜猜就可以了。你只要知道MSIL是基于栈的语言,那差不多能猜出来ldc.i4.5是往栈顶push个5的。

    在MSIL中只有类、方法、字段

    不管高层的语言,比如C#、VB.NET提供多少绚丽多彩的程序元素,比如委托、事件、属性。但是在MSIL中只有类、方法、字段这三种程序元素。而这些“神奇”的元素最终都依靠语言各自的编译器生成这三个元素(当然还有一些元数据)。

    MSIL难么?难,如果你要知道每条命令的意思是什么,真的很难。MSIL容易学么?容易,把上面的文字再仔细看看就OK了,对于日常分析足够了。

    2、 Reflector

    大名鼎鼎的Reflector不用多说了,是居家必备。

    3、 Debuger(Visual Studio+SOS.dll)

    上面两个工具虽然很重要,但是对于一些底层的东西就爱莫能助了,这个时候我们就需要Debuger了。在Windows里首推的调试器是WinDbg。WinDbg相当的强大,不仅仅可以进行User Mode的调试,还可以进行Knerl Mode的调试。但是功能强大,必定使用起来也很麻烦,直到现在我还不能熟练的使用WinDbg。幸运的是,Visual Studio也能提供Debug的功能。

    要调试.Net的程序,我们还需要一个SOS.dll的扩展。全称是Son of Strike(不知道为啥要起这么一个奇怪的名字,Strike是微软内部使用的,而SOS.dll提供的是Strike的一个子集,但是现在SOS.dll的功能基本上与Strike提供的功能相当,所以这个名字也失去了它原来的意义 感谢装配脑袋的解释)。SOS可以帮助Visual Studio读取.Net的数据结构。在这一节里,我们就以实例的方式学习Visual Studio+SOS.dll的使用。

    我们以一个非常简单的Console程序作为“解剖”的程序。在Visual Studio(我使用的是Visual Studio Team System 2008英文版,其他版本类似)创建一个Console类型的项目后,第一步在项目属性窗口的“调试(Debug)”选项卡里选中“允许非托管代码调试(Enable unmanaged code debuging)”

    输入以下代码:

       1: using System;
       2: namespace Yuyijq.DotNet.Chapter2
       3: {
       4:     class StudyDebuger
       5:     {
       6:         static void Main()
       7:         {
       8:             int[] intArr = new int[5];
       9:             intArr[0] = 3;
      10:             intArr[1] = 5;
      11:         }
      12:     }
      13: }

    在Main方法第二行设置断点,F5启动调试。命中断点后,我们在立即窗口(Immediate Window)(打开立即窗口:菜单栏->调试(Debug)->立即窗口(Immediate))里输入命令:

    .load sos.dll

    这个命令用于加载sos扩展,sos.dll位于.Net Framework安装的目录中,如果你没有设置环境变量,那就需要在这里输入sos.dll的完整路径。

    加载sos扩展之后,就可以享用sos的诸多命令了,sos的所有命令都已感叹号“!”开头,在这里我不准备介绍sos的命令,你可以使用!help获得sos所有命令的列表,然后你还可以通过!help 命令名的方法时获得每个命令的详细介绍,介绍中不仅仅有使用方法,还有示例。不需要弄明白所有命令的使用方法,常用的就这么几个:

    !U,!DumpStackObjects(dso),!DumpObject(do),!DumpHeap,!DumpMT,!DumpMD,!DumpStack,!ObjSize,

    !DumpDomain,!Name2EE,!DumpClass,!Threads

    使用这些命令,基本上就可以印证很多书上跟你说的事儿是不是真的,看看他是不是在“胡扯”,而且你这么一动手,自己亲自印证了以后跟看书获得的资讯完全不是一回事儿。

    在后面的内容中,我会常常使用这些命令来探索一些细节内容。

    除了在Visual Studio的立即窗口输入命令,Visual Studio还有一个内存窗口(Debug->Window->Memory),微软想得很周到,有四个内存窗口,你可以同时看好几块内存。

    还是上面那块示例代码,我们来看看内存窗口的作用

    .load sos.dll

    extension C:\Windows\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded

    !dso

    PDB symbol for mscorwks.dll not loaded

    OS Thread Id: 0xa2c (2604)

    ESP/REG Object Name

    002df05c 01b928a4 System.Int32[]

    通过上面的命令,我们发现intArr数组的首地址为01b928a4。打开内存窗口,在Address一栏输入:0x01b928a4(千万记住,这里的0x不能掉了),显示如下:

    clip_image002[8]

    为了观察方便,我们把内存窗口的Columns设为8,这样就比较对齐了。知道Object Layout的童鞋(我以前的文章也有介绍),应该知道每个Object有两个附加字段:同步块索引和方法表指针,这个01b928a4应该指向的就是方法表指针的位置,那上面内存窗口显示的5c aa c9 6f那就应该是方法表指针了,这里我们不关注这个。但是看到后面居然有一个5,这个难道跟数组的大小有关系么?(猜测,只是猜测)。

    F10继续执行,我们发现内存窗口将内存有变化的地方用红色标识出来了,真是太方便了(我想骂一句娘)。

    clip_image004[8]

    (这个3不是数组第一个元素的值么)

    F10再执行

    clip_image006[6]

    (这个5不是数组元素的第二个值么)

    聪明的你应该有这样的猜测:

    数组的内存布局是这样的,紧跟在方法表指针后面的是数组的大小,然后是数组的元素的值(按顺序排列)。

    唔,我不知道这是不是正确的,那好,我多试几次,发现我的猜测貌似是正确的。

    你看,上面这种探索的过程,一定会让你难忘,比你看书,看作者在那里“胡侃”要记忆深刻得多。

    除了内存查看窗口,Visual Studio还有寄存器查看窗口(Debug->Window->Registers),还有Threads,Modules等等,等着你去尝试。

    后记

    有了这三个工具,我相信对你的学习和帮助一定是如虎添翼。要详细的介绍这些工具,需要大量的篇幅,但是我觉得我只给一个引子,剩下的留给你自己去探索,岂不是更有意思。由于时间和水平有限,难免有不对之处,如有发现请一定要告诉我,谢谢。

  • 相关阅读:
    Atitit fms Strait (海峡) lst 数据列表目录1. 4大洋 12. 著名的海大约40个,总共约55个海 13. 海区列表 23.1. 、波利尼西亚(Polynesia,
    Atitit trave islands list 旅游资源列表岛屿目录1. 东南亚著名的旅游岛屿 21.1. Cjkv 日韩 冲绳 琉球 济州岛 北海道 21.2. 中国 涠洲岛 南澳
    Atitit Major island groups and archipelagos 主要的岛群和群岛目录资料目录1. 岛群 波利尼西亚(Polynesia, 美拉尼西亚(Melanesia,
    Atitit glb 3tie city lst 三线城市列表 数据目录1. 全球范围内约90个城市 三线 12. 世界性三线城市全球共
    Atitit glb 1tie 2tie city lst 一二线城市列表数据约50个一线城市Alpha ++ 阿尔法++,,London 伦敦,,New York 纽约,,Alpha +
    Attit 现代编程语言重要特性目录第一章 类型系统 基本三大类型 2第一节 字符串 数字 bool 2第二节 推断局部变量 2第三节 动态类型 2第二章 可读性与开发效率 简单性 2
    Atitit 未来数据库新特性展望目录1. 统一的翻页 21.1. 2 Easy Top-N
    使用Chrome DevTools(console ande elements panel)进行xpath/css/js定位
    chrome -console妙用之定位xpath/js/css
    表达式树之构建Lambda表达式
  • 原文地址:https://www.cnblogs.com/yuyijq/p/1583709.html
Copyright © 2011-2022 走看看