理解.NET程序集的执行过程

    对于一个已编译好的.NET程序集,Windows操作系统是如何启动执行的呢?日常使用中我们发现对于托管的和非托管的程序集编译器都会吧程序集编译成以.exe或.dll等为扩展名的文件,可见Windows加载器并没有区分是托管还是非托管的程序集,而且我们也知道对非托管的程序集是在编译器直接编译成了机器码,自然可以由CPU直接执行,而托管的.NET 程序集是包含复杂结构的MSIL代码,执行时会使用JIT即时编译器将IL代码编译成机器码,再由CPU执行,当然这期间还需要执行其它许多的工作,如加载CLR、执行初始化等工作,那么这些是怎么自动实现的呢?理解这些问题是我们深入.NET的关键,由于日常的开发工作并不涉及这些知识(编译器已经给我们做了),结果是很多的.NET 书忽略了这一点,也很少有人研究者方面的内容,所以在阅读了《.NET 高级调试》这本书给了我非常清晰地认识,把里面的这方面精彩的内容做了一个总结以供那些还没有清楚.NET 程序集如何执行的学者们一个简单的参考。

    首先我们要清楚的是对于托管还是非托管程序集,他们在编译器执行编译时都会编译成一个特殊的文件格式,即PE文件(可移植可执行文件格式),操作系统加载器通过加载这样的PE文件来执行程序集的。可以这么说吧,无论是托管程序还是非托管程序他们实际上都是编译成这样的PE文件(只是有部分内容不一样而已)。

    然后这个PE文件会指示如何执行托管程序集和非托管程序集,加载器首先会查找到PE头中的AddressOfEntryPoint域,这个域指示PE文件的入口点位置,在.NET程序集中是指向.text段中的CLR头--〉包含一个结构IMAGE_COR20_HEADER—>包含许多信息如托管代码应用程序的入口点,目标CLR的主版本号和从版本号,以及程序集的强名称签名等--〉Windows加载器根据这个数据结构决定加载哪个版本的CLR以及一些基本的程序集信息。在.text段中还包含了程序集的元数据表,MSIL以及非托管启动存根代码,而非托管启动存根代码包好了由Windows加载器执行役启动PE文件执行的代码,结构如图所示。

图片2

   这样.NET 程序集的加载算法包括:

     1、用户执行一个.NET程序集;

     2、Windows加载器查看AddressOfEntryPoint域,并找到PE映像文件的.text段;

     3、位于AddressOfEntryPoint位置上的字节只是一个JMP(跳转)指令,这个指令跳转到mscoree.dll中的一个导入函数;

     4、将执行控制转移到mscoree.dll中的_CorExeMain中,这个函数将启动CLR并把执行控制转移到程序集的入口点。

posted @ 2011-05-29 23:29  suyan010203  Views(2294)  Comments(8Edit  收藏  举报