来自:https://www.cnblogs.com/dwlsxj/p/MSIL.html
一、开篇

  研究MSIL纯属于个人喜好,说在前面MSIL应用于开发的地方很少,但是很大程度上能够帮着我们理解底层的原理,这是我了解MSIL的主要原因。托管代码表示应用程序的方法的功能,它们以微软的中间语言(Microsoft intermediate language,MSIL)或公共语言运行(common intermediate language,CIL)的抽象二进制形式进行编码。

  MSIL代码由CLR“托管”。CLR托管至少包括三个主要的活动:类型控制,结构化异常和垃圾收集。类型控制设计在执行期间项类型的验证和转换。托管异常处理在功能上与“非托管的”结构化异常处理类似,但它是由CLR执行的而不是有操作系统执行的。垃圾收集涉及对不再使用的对象进行自动标识和释放。

  在CLR环境下,.NET应用程序由一个或多个托管可执行体组成,其中每一个都携带着源数据和托管代码。托管的可执行体成为模块,主要包括两个组件:元数据和MSIL代码。CLR处理这两个组件的主要子系统是加载程序(Loader)和JIT(Just-in-time,即时)编译器。

  接下来我们主要讲的是托管可执行体里面的MSIL(中间语言),再讲MSIL时,先把微软的整个框架体系简单概括下:(见下图一)

  简要概述下整个过程,首先是我们编写的C#源文件hello.cs通过C#编译器进行编译,编译成.NET 的PE文件结构,也就是exe文件格式,当程序运行时,Windows的Loader加载器不会负责该程序的内存分配,线程管理等工作,而是只负责跳转到CLR的执行引擎(EE)中,将控制权交由CLR,由CLR进行分配内存,线程管理,异常处理等。

  通过查看.NET 的PE文件导入表中只有一个API,exe对应mscoree.dll的_CorExeMain;而dll对应mscoree.dll的_CorDllMain。这就说明windows的loader加载器载入.NET PE后,只负责跳转到相应的DLL,随后改程序边运行在EE的监管中。查看导入表如下图所示:

二、初探MSIL

  我觉得学习每一项知识时,一些技巧性的东西是靠一步一步去积累的,但是我认为底层的探索也是学习当中必不可缺的一部分。有些时候底层的知识可以呈现出原理性的东西,越接近底层的知识就越靠近实现部分,好了废话也不多说接下来我们就来探讨MSIL中间语言,MSIL中间语言是基于堆栈的面向对象语言。

  借助一个简单的例子进行分析MSIL中间语言,每个编程技术人员都是从Hello World开始那我们也从Hello World开始。 

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 string hello = "Hello World";
6 Console.WriteLine(hello);
7 Console.Read();
8 }
9 }

  输出结果很明显是:Hello World,如下图所示:

  

  接下来我们将要分析该程序的MSIL代码,通过ILDASM.EXE工具将exe文件进行反编译成MSIL中间语言。如下图所示:

  简要概述:

  关键字:.method表示方法的意思,.method private hidebysig static void  Main(string[] args) cil managed表示的意思就是static void main(string[] args)

  .entrypoint标志方法的入口

  .maxstack表示分配堆栈大小

  .locals init中存放的是当前方法的局部变量,这里面是string类型,它的名称叫hello。Init指令表示对变量应以对应的类型默认值进行初始化,通常情况下变量名可以省略,在代码中将以零基索引来引用

  例如:stloc.0表示将Envaluation Stack中的一个栈顶数值保存到局部变量0(Call Stack)中。

  先介绍几个关于MSIL内部知识点:

  ①.Managed Heap:这是动态配置(Dynamic Allocation)的记忆体,由 Garbage Collector(GC)在执行时自动管理,整个 Process 共用一个 Managed Heap,可以理解为引用类型的东西都放在这个Managed Heap中。

  ②.Call Stack:这是由 .NET CLR 在执行时自动管理的记忆体,每个Thread都有自己的Call Stack堆栈。每调用一次method,就会使得Call Stack上多了一个Record Frame;调用完毕之后,此Record Frame会被丢弃。一般来说,Record Frame内记录着method参数(Parameter)、返回位址(Return Address)、以及局部变量(Local Variable)。.NET CLR都是使用0, 1, 2…编号的方式来识别局部变量。

  ③.Evaluation Stack:这是由.NET CLR在执行时自动管理的记忆体,每个Thread都有自己专属的Evaluation Stack。压入的到Evaluation Stack的值,当方法调用结束时必须保持这个堆栈的平衡,这里面存放例如局部变量值,以及引用类型的地址。

  指令ldc是将参数存储至堆栈Evaluation Stack

  指令stloc是将变量存储至堆栈Call Stack

  技巧:ld开头就是加载数据到Evaluation Stack中,而st开头就是将Envaluation Stack中的数据保存到Call StackCall Stack存放局部变量值。

  接下来我们将演示代码的堆栈情况。

  首先进入的是IL_0000段的代码为nop,这段代码表明了没有任何操作。

  

  接下来就要到了IL_0001段代码为ldstr “hello World”,ldstr加载字符串是将字符串的引用放在了Envaluation Stack中,而真正的字符串放在了Managed Heap中,详情请见下图:

  

  接下来就要运行到了stloc.0这条指令的意思就是讲参数保存到局部变量中。

  

  将Envaluation Stack中的值保存到 Call Stack中,因为Envaluation Stack中存放的是“hello World”字符串的地址,所以V0存放的也是字符串的地址。

接下来要运行到了ldloc.0加载到Envaluation Stack中局部变量0的地址。

  接下来运行MSIL的call语句,从 Evaluation Stack 中取出一个值,此值为 Reference Type,调用 mscorlib.dll 所提供的 System.Console::WriteLine(string),注意这里用的call,因为这个是静态方法(static method),而不能用CallVirt方法。结构图如下所示:

  接下来就要调用静态方法System.Console::Read()等待用户输入之后,将输入值放入到Envaluation中去,最后再用pop指令将数值从Envaluation Stack中弹出来,最后就到达了ret这个地方,此指令的意思是:结束此次调用(也就是 Main 的调用)。此时会检查 Evaluation Stack 内剩下的资料,由于 Main() 告知不需要传出值(void),所以 Evaluation Stack 内必须是空的,本范例符合这样的情况,所以此时可以顺利结束此次调用。而 Main 的调用一结束,程序也随之结束。

三、结束语

  通过这篇文章可以清晰的了解MSIL中间语言的运行机制,是基于堆栈的形式操作。再次声明学习MSIL只是由于个人兴趣,希望各位能够提出宝贵的意见以及上述有错的地方能够指正,小丁虚心求教。

浅析MSIL中间语言——基础篇(转)的更多相关文章

  1. 浅析MSIL中间语言——基础篇

    一.开篇 研究MSIL纯属于个人喜好,说在前面MSIL应用于开发的地方很少,但是很大程度上能够帮着我们理解底层的原理,这是我了解MSIL的主要原因.托管代码表示应用程序的方法的功能,它们以微软的中间语 ...

  2. 浅析MSIL中间语言——PE文件结构篇

    一.开篇 开篇我想讲一下于本文无关的话题,其实我很想美化一下自己博客园一直没时间弄,无意间找了博客园李宝亨的博客园里面有一篇分享自己主题的文章,我就将这个模板暂时用作我的blog主题,我要讲述一个关于 ...

  3. python之路基础篇

    基础篇 1.Python基础之初识python 2.Python数据类型之字符串 3.Python数据类型之列表 4.Python数据类型之元祖 5.Python数据类型之字典 6.Python Se ...

  4. Sass进阶之路,之一(基础篇)

    Sass 学习Sass之前,应该要知道css预处理器这个东西,css预处理器是什么呢? Css预处理器定义了一种新的语言将Css作为目标生成文件,然后开发者就只要使用这种语言进行编码工作了.预处理器通 ...

  5. C#多线程之基础篇3

    在上一篇C#多线程之基础篇2中,我们主要讲述了确定线程的状态.线程优先级.前台线程和后台线程以及向线程传递参数的知识,在这一篇中我们将讲述如何使用C#的lock关键字锁定线程.使用Monitor锁定线 ...

  6. 一步步学习javascript基础篇(0):开篇索引

    索引: 一步步学习javascript基础篇(1):基本概念 一步步学习javascript基础篇(2):作用域和作用域链 一步步学习javascript基础篇(3):Object.Function等 ...

  7. 2000条你应知的WPF小姿势 基础篇<15-21>

    在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师,对C#和WPF有着极深的热情.最为出色的是他维护了两个博客:2,000Things You Should Know ...

  8. ABP框架实践基础篇之开发UI层

    返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 说明 其实最开始写的,就是这个ABP框架实践基础篇.在写这篇博客之前,又回头复习了一下ABP框架的理论,如果你还没学习,请查看AB ...

  9. C#多线程之基础篇2

    在上一篇C#多线程之基础篇1中,我们主要讲述了如何创建线程.中止线程.线程等待以及终止线程的相关知识,在本篇中我们继续讲述有关线程的一些知识. 五.确定线程的状态 在这一节中,我们将讲述如何查看一个线 ...

随机推荐

  1. OpenGL之shader着色器的应用,三色渐变的三角形

    学习自: https://learnopengl-cn.github.io/01%20Getting%20started/05%20Shaders/#_7 首先放一张效果图: 本次教程,将着色器单独定 ...

  2. mysql 5.7安装图解 mysql 5.7图文安装完整教程

    今天给搭建分享一个教程,mysql 5.7的安装操作,这里呢我叫大家怎么用二进制去安装mysql,其实在大多数的生产环境中使用二进制预编译的安装方式是最多了,下面大家跟着我的步骤去尝试着安装下吧. 先 ...

  3. VIM学习二: VIM配置代码及效果图

    vim学习及插件 参见:http://www.cnblogs.com/caixu/p/6337926.html .vimrc配置 "***************************** ...

  4. GSM:嗅探语音流量

    GSM: Sniffing voice traffic I wrap up the GSM series with a walkthrough on how to decrypt voice traf ...

  5. 运维ps语法---》ps、pstree、top、htop、nice、renice、kill、ulimit、w 和 who 和 whoami、pgrep、fg 和 bg、ipcs

    Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程信 ...

  6. js 防抖 debounce 与 节流 throttle

    debounce(防抖) 与 throttle(节流) 主要是用于用户交互处理过程中的性能优化.都是为了避免在短时间内重复触发(比如scrollTop等导致的回流.http请求等)导致的资源浪费问题. ...

  7. HTML+CSS水平垂直居中

    啦啦啦,好了,今天来分享自己的第一个知识点,难得自己还能想起来过来博客园,写写博客的. 好了,言归正传,今天分享关于html和css的一个简单的知识点,对于大部分从事前端开发的人员来说可能都是很简单的 ...

  8. Raspberry Pi 安装配置 Home Assistant

    家庭助理(Home Assistant)是一款基于 Python 的智能家居开源系统,支持众多品牌的智能家居设备,可以轻松实现设备的语音控制.跟踪和控制家里的所有设备, 并自动化控制,能完美的运行在树 ...

  9. 显示react配置

    1. 由于react默认隐藏webpack配置需要手动显示. npm run eject //Are you sure you want to eject? This action is perman ...

  10. 学习笔记:分布式日志收集框架Flume

    业务现状分析 WebServer/ApplicationServer分散在各个机器上,想在大数据平台hadoop上进行统计分析,就需要先把日志收集到hadoop平台上. 思考:如何解决我们的数据从其他 ...