.NET CRL程序载入原理大揭秘

开发 后端
本文介绍了.NET CRL程序载入原理分析,在一个.NET CRL下的可执行程序的启动过程可以分为三个步骤,哪三个步骤本文给出全面的解析。

.NET这个词语对我们不陌生吧,而.Net平台下CLR程序程序怎么载入呢?这个有有些人就步知道了,在这里给大家分析以下原理。与传统的Win32可执行程序中的本机代码(Native Code)不同,微软推出的.Net架构中,可执行程序的代码是以类似Java Byte Code的 IL (Intermediate Language)伪代码形式存在的。在.Net可执行程序载入后,IL代码由CLR (Common Language Runtime)从可执行文件中取出, 交由JIT (Just-In-Time)编译器,根据相应的元数据(Metadata),实时编译成本机代码后执行。
 
因此,一个.NET CRL下的可执行程序的启动过程可以分为三个步骤。
首先,Windows的可执行程序载入器(OS Loader)载入 PE (Portable Executable)结构的可执行文件映像(PE Image),将执行权传递给CLR的支持库中的Unmanaged Code。
其次,启动或使用现有的CLR引擎,建立新的应用域(Application Domain),将配件(Assembly)载入到此应用域中。
最后,将执行权从Unmanaged Code传递给Managed Code,执行配件的代码。

下面我将详细说明以上步骤。
 
自从Win95发布以来,可执行程序的PE结构就没有发生大的改动。此次.Net平台发布,也只是利用了PE结构中现有的预留空间,以保持PE结构的稳定,最大程度保持向后兼容。CLR程序在编译后,将可执行程序入口直接以一个间接跳转指令 ,指向mscoree.lib中的_CorExeMain函数(DLL将入口指向_CorDllMain函数)。因此CLR可执行程序在被OS Loader载入后,将_CorExeMain函数处理CLR引擎 ,启动事宜。此函数将启动或使用一个现有的CLR Host来加载IL代码。常见的CLR Host有ASP.Net、IE、Shell、数据库引擎等等,他们的作用是启动一个CLR实例,管理在此CLR实例中运行的CLR程序。我们接着来看一看一个CLR Host是如何实际运作的。

CLR作为一个引擎,在同一台计算机上是可以存在多个版本的,不同版本之间可以通过配置良好共存。在 %windir%\Microsoft.NET\Framework (%windir%表示Windows系统目录所在位置)目录下我们可以看到以版本号为目录名的多个CLR版本, 如%windir%\Microsoft.NET\Framework\v1.0.3705等等,也可以在注册表的

  1. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\policy\v1.0  

键下查看详细的版本兼容性.Name是Build号,Value是兼容的Build号. 而每一个CLR版本又分为Server和Workstation两类运行库, 我们等会讲创建.NET CLR时会详细谈到. CLR Host在启动CLR之前,必须通过一个startup shim的库进行操作, 实际上就是mscoree.dll,他提供了版本无关的操作函数,以及启动CLR所需 的支持,如CorBindToRuntimeEx函数. CLR Host通过shim的支持库,将CLR引擎载入到进程中.具体函数如下

  1. STDAPI CorBindToRuntimeEx(LPCWSTR pwszVersion,   
  2. LPCWSTR pwszBuildFlavor, DWORD startupFlags,   
  3. REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv);  

参数pwszVersion指定要载入的CLR版本号,注意必须在前面带一个小写的"v", 如"v1.0.3705",可以通过查阅前面提到的注册表键,获取当前系统安装的不同CLR 版本情况,或指定固定的CLR版本.也可以递NULL给这个参数,系统将自动选择最新版本的CLR载入. 参数pwszBuildFlavor则指定载入的CLR类型,"srv"和"wks". 前者适用于多处理器的计算机,能够利用多CPU提高并行性能.对单CPU系统而言,无论指定哪种类型都会载入"wks",传递NULL也是如此. 参数startupFlags是一个组合参数.由多个标志位组成. STARTUP_CONCURRENT_GC标志指定是否使用并发的GC(Garbage Collection) 机制,使用并发GC能够提高系统的用户界面相应效率,适合窗口界面使用较多的程序. 但并发GC会因为无谓的线程上下文(Thread Context)切换损失效率.

以下三个参数用于指定配件载入优化策略.我们等会详细讨论.

  1. STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN = 0x1 << 1,   
  2. STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN  = 0x2 << 1,   
  3. STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN_HOST = 0x3 << 1,  

接着的三个参数用于获取ICorRuntimeHost接口.

实际调用实例如下.

  1. CComPtr<ICorRuntimeHost> spHost;   
  2. CHECK(CorBindToRuntimeEx(NULL, L"wks",   
  3. STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN | STARTUP_CONCURRENT_GC,   
  4. CLSID_CorRuntimeHost, IID_ICorRuntimeHost, (void **)&spHost)); 

这行代码载入最高版本CLR的wks类型运行库,为单应用域进行优化并使用并发GC机制. 前面提到了配件载入优化策略,要理解这个概念,我们必须先了解应用域的概念. 传统Win程序中,资源的分配管理单位是进程,操作系统以进程边界将应用程序实例隔离开, 单个进程的崩溃不会对其他进程产生直接影响,进程也不能直接使用其他进程的资源. 进程很好,但使用进程的代价太大,为此Win32引入了线程的概念.同一进程中的线程能够共享资源,线程管理和切换的代价也远远小于进程.但因为在同一进程中,线程的崩溃会直接影响到其他线程的运行,也无法约束线程间数据的直接访问等等. 为此,CLR Application Domain应用域的概念.应用域是介于进程和线程之间的一种逻辑上的概念.他既有线程轻巧,管理切换快捷的优点,也有进程在稳定性方面的优点,单个应用域的崩溃不会直接影响到同一进程中的其他应用域,应用域也无法直接访问同一进程中的其他应用域的资源,这方面和进程完全相同. 而.NET CLR的管理就是完全面向应用域一级.CLR不能卸载(Unload)某个类型或配件, 必须以应用域为单位启动/停止代码,获取/释放资源.

CLR在执行一个配件时,会新建一个应用域,将此配件放入新的应用域.如果多个应用域同时使用到一个配件,就要涉及到前面提到的配件载入优化策略了.最简单的方法是使用

STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN标志,每个应用域拥有一份独立的配件的镜像,这样速度最快,管理最方便,但占用内存较多.相对的是所有应用域共享一份配件的镜像,(使用STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN标志) 这样节约内存,但在此配件中存在静态变量等数据时,因为要保证每个应用域有独立的数 据, 所以会一定程度上影响效率.折中的方案是使用 (使用STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN_HOST标志) 此时,只有那些有Strong Name的配件才会被多个应用域共享.

这里又涉及到一个概念Strong Name.他是一个配件的身份证明,他由配件的 名字/版本/culture以及数字签名等组成.在配件发布时用以区别不同版本. 也在安全/版本控制等方面起到重要作用,以后有机会会专门讲解.暂且跳过. 获取了ICorRuntimeHost接口的指针后,我们可以以此指针取得当前/缺省应用域,
并可枚举CLR引擎实例中所有的应用域.

【编辑推荐】

  1. SQL SERVER 2005 CLR集成之简单SQL函数讲解
  2. 代码演示CLR Via C#如何调用静态构造函数
  3. 概述讲解SQL Server 2005 CLR集成执行功能
  4. 简单概述SQLCLR CAS权限集
  5. 全面讲解CLR安全性
责任编辑:田树 来源: 教程在线
相关推荐

2010-02-02 18:34:00

Python

2009-11-04 13:51:46

ADO.NET性能

2009-10-29 10:34:31

ADO.NET使用技巧

2023-06-26 18:03:26

btrace 2.0开源

2021-01-18 18:15:00

GitHub 技术开发

2010-01-22 14:53:21

2018-09-18 15:57:44

机器学习ML神经网络

2009-11-10 11:01:05

VB.NET事件

2021-05-13 23:30:17

JavaScript 原理揭秘

2017-07-05 16:43:52

VSAN加密虚拟化

2016-02-29 16:54:10

OpenStack混合云应用软件定义基础设施

2017-08-24 09:19:20

分解技术揭秘

2017-07-06 08:21:27

VSAN加密虚拟机

2009-05-28 10:12:04

2013-04-22 11:13:06

程序员编程误区

2019-11-15 15:12:19

Windows激活KMS

2020-12-28 09:50:50

Python内存管理语言

2010-08-09 09:56:12

FlexBuilder

2009-12-17 16:53:13

.NET Framew

2009-11-03 17:25:59

ADO.NET编程技巧
点赞
收藏

51CTO技术栈公众号