.net 程序运行过程

.net程序的产生过程:

高级语言代码(c# or c++.net or vb.net)由各自的编译器编译为“托管模块”。这个托管模块是只能由clr执行的标准windows可移植可执行(portable,executable简称PE)文件。这个托管模块(PE文件)由以下几个部分组成:

  1. PE表头。该表头指出了文件的类型,是GUI(图形用户界面),还是CUI(控制台用户界面),或者是DLL链接库。另外该表头中还含有一个时间标记用于表示该文件创建的时间。
  2. CLR表头。用来表示该托管模块所需要的clr版本信息,主入口方法(Main)及其他托管模块。
  3. 元数据。用于描述源代码中定义或者引用的类型和成员。
  4. IL代码。编译器在编译源代码时产生的指令。CLR在运行时会将编译成本地代码执行。

而不同的托管模块又被一个程序集(Assembly)组织。

.net程序执行的过程:

在xp之前(CLR还没有发布)的时候,window2000等操作系统执行.net程序时,首先window系统加载器像对待非托管代码一样加载程序。第一步就是检查文件的idata文件头部分,发现需要加载MSCorEE.dll到进程的地址空间,然后MSCorEE.dll找到_CoreExeMain函数的地址,并修改托管Exe中的sub函数的JMP指令,修正的依据就是.net编译器在编译的时候向托管exe的文件头.text部分加上的6字节的sub函数JMP _CoreMain。有了这个依据,MSCoreEE就会立即执行 _CoreMain方法,而该方法就会初始化CLR后立即返回,继续一般非托管的流程。而托管代码的运行就是CLR的事情了。

而在xp及以后的系统上,系统加载器会判断pe文件头的第14条目录上检测是否包含托管代码。如果该目录项存在且不为0,那么加载器会自动跳转到MSCoreEE.dll的相应方法上初始化clr。

上面涉及到的只是clr被初始化的过程,下面说说clr初始化后为了运行托管程序又做了什么工作?!

Clr被初始化后,首先会从PE的Clr头文件中检测到主程序的地址。在主程序方法执行之前,CLR会检测Main中代码引用到的所有类型。这时,clr就会分配一个内部的数据结构用来保存这些类型的访问地址。而每个类型CLR会为他分配一个单独的内部结构,在这个内部结构中每个方法都有一个条目,这个条目保存着这个方法的实现地址。在初始化的时候这些地址都还没有,而是被默认的设置为clr内部一个没有正式记录的函数(JITCompiler)。

当具体方法被第一次调用的时候,首先调用的就是JITCompiler,因为JITCompiler知道是哪个函数被调用了,所以他就会去需找该函数的IL代码并将它们编译为本地代码(这个过程叫做JIT,即时编译  just-in-time)保存的一个动态内存中,同时将该条目的实现地址改为这个内存块的地址,这样该方法下次被调用的时候就会立即执行本地代码而不需要再次编译了。这也是为什么.net程序第一次启动的时候比较慢,后来就快了的原因所在。由于本地代码保存在一个动态的内存中,所以程序被关闭后,这个动态内存也被释放。下次重新启动程序仍会首先即时编译,正如上面的过程一样。

原文地址:https://www.cnblogs.com/qianlifeng/p/1819661.html