欢迎来到.net学习网

欢迎联系站长一起更新本网站!QQ:879621940

您当前所在位置:首页 »  .NET本质论第一卷:公共语言运行库教程 » 正文

本教程章节列表

CLR加载器

创建时间:2012年08月23日 14:28  阅读次数:(6975)
分享到:

CLR加载器负责加载和初始化程序集、模块、资源以及类型。CLR加载器总是尽可能少地加载和初始化。与Win32加载器不同,CLR加载器不会处理与自动加载从属模块(或者程序集)。准确地说,从属部分只有当它们确实被需要时才会按需加载(类似于Visual C++ 6.0的延迟加载特征)。这样,不仅加速了程序的初始化时间,也减少运行程序所消耗的资源。

在CLR中,加载通常是由基于类型的JIT编译器(JIT编译器:有时也做实时编译器、及时编译器或者即时编译器。前者为Microsoft .NET帮助的译法,后两种多见于Java的书籍。)[just in time(JIT) compiler]触发的。当JIT编译器试图将方法体由CIL转化为机器码时,它需要访问声明类型的类型定义,以及用于类型字段的类型定义。此外,JIT编 译器还需要访问将被实时编译的局部变量或者方法参数所用到的类型定义。加载类型意味着包含类型定义的程序集和模块都要被加载。

按需加载类型(以及程序集和模块)的策略是意味着:程序中未被使用的部分将不会被载入内存中。同时也意味着:正在运行的应用程序在执行过程中,经常会遇到新程序集和新模块被加载的情形,因为应用程序需要它们当中的类型。如果你不想这么做,那么,你将有两个选择:一种方式就是简单地声明相关类型的隐藏静态字段,使相关类型在你的类型被加载的同时也随即被加载;另一种方式就是显式地操纵加载器。

加载器往往是隐式地为你工作。开发人员能够通过程序集加载器(assembly loader)显式地与加载器进行交互。程序集加载器通过System.Reflection.Assembly类的LoadFrom静态方法向开发人员公开。这个方法接受了一个CODEBASE字符串,它既可以是文件系统路径,也可以是识别含有程序集清单模块的统一资源定位器[uniform resource locator(URL)]。如果不能找到指定文件,加载器将会抛出System.FileNotFoundException异常。如果找到了指定文件,却不是程序集清单中的CLR模块,加载器将会抛出一个System.BadImageFormatException异常。最后,如果CODEBASE是一个模式而不是file:的URL,那么,调用方必须具备WebPermission访问权限,否则将抛出System.SecurityException异常。此外,URL(而不是file:协议)的程序集将在被加载之前,先下载到缓存中。

示例2.2展示了一个简单的C#程序。它将加载位于file://C:/usr/bin/xyzzy下的一个程序集,然后创建名为AcmeCorp.LOB.Customer的包含类型的一个实例。在这个例子中,调用方提供的是程序集的物理位置。当程序按照这种方式使用程序集加载器时,CLR便忽略了程序集的四部名字,包括它的版本号。
示例2.2:以显式的CODEBASE加载程序集

using System;
using System.Reflection;
public class Utilities {
public static Object LoadCustomerType() {
Assembly a = Assembly.LoadFrom(
"file://C:/usr/bin/xyzzy.dll");
return a.CreateInstance("AcmeCorp.LOB.Customer");
}
}

尽管通过位置加载程序集很有意思,但是,大多数程序集是通过程序集解析器(assembly resolver)按名字加载的。程序集解析器使用四部程序集名字,从而决定哪个程序集将由程序集加载器加载到内存中。如图2.9所示,从名字到位置的解析过程考虑了各种因素,包括应用程序的所在目录、版本控制策略以及其它配置细节(这些将在本章后面讨论)。
程序集解析器通过System.Reflection.Assembly类的Load方法向开发人员公开。如示例2.3所示,这个方法接受一个四部程序集名字(要么是一个字符串,要么作为一个AssemblyName引用),并且它与程序集加载器公开的LoadForm方法很相似。这种相似性仅仅只停留在表面上,因为Load 方法先使用程序集解析器,通过一些稍微复杂的操作,才能找到适合的文件。这些操作的第一个就是运用版本策略,确定将被加载程序集的版本。
示例2.3:使用程序集解析器加载程序集
using System;
using System.Reflection;
public class Utilities {
public static Object LoadCustomerType() {
Assembly a = Assembly.Load(
"xyzzy, Version=1.2.3.4, " +
"Culture=neutral, PublicKeyToken=9a33f27632997fcc");
return a.CreateInstance("AcmeCorp.LOB.Customer");
}
}


图2.9:程序集的解析和加载

程序集解析器首先采用可能有效的版本策略(version policy),开始它的工作。版本策略将导致程序集解析器的重定向,从而能够加载所需程序集的替代版本。版本策略能够将给定程序集的一个或多个版本映射为不同版本;然而,除了版本之外,版本策略不能使解析器重定向名字不同的程序集(例如,一个名为Acme.HealthCare的程序集,不能重定向为名为Acme.Mortuary的程序集)。特别要注意版本策略只能应用于由四部程序集名字完全限定的程序集。如果程序集名字只是部分限定的(例如,缺少公钥标记、版本或文化),那么,不能使用任何版本策略。同样,如果绕过程序集解析器,直接调用Assembly.LoadForm方法,那么,也不会运用什么版本策略了。因为你指定了物理路径,而不是程序集的名字。

版本策略可以通过配置文件被指定。这些配置文件包括一个计算机范围的配置文件,以及一个特定应用程序的配置文件。计算机范围(machine-wide)的配置文件总是被命名为machine.config,而且,存于%SystemRoot%\Microsoft.Net\Framework\V1.0.nnnn\CONFIG目录下。特定应用程序(Application-specific)的配置文件总是在应用程序的APPBASE目录下。对于基于CLR的.EXE程序,APPBASE是用于定位的基本URI(或者目录),可执行文件就是从这里被加载的。对于ASP.NET应用程序,APPBASE是Web应用程序虚拟目录的根结点。对于基于CLR的.EXE程序,其配置文件的名字与可执行文件的名字相同,只是配置文件名的后缀为“.config”。例如,如果启动的CLR程序集为C:\myapp\app.exe,那么,相应的配置文件将是C:\myapp\app.exe.config。至于ASP.NET应用程序,其配置文件总是被命名为web.config。

配置文件以可扩展标记语言[Extensible Markup Language(XML)]为基础,而且,总有一个名为configuration的根元素。配置文件将被程序集解析器、远程调用基础架构和ASP.NET所使用。对于用于配置程序集解析器的元素,图2.10展示了其基本模式。所有相关元素都是在assemblyBinding元素下,其名字空间为urn:schemas-microsoft-com:asm.v1。这些应用程序范围(Application-wide)的设置控制了探测路径以及发行者版本策略模式(在本章后面都将有所描述)。此外,dependentAssembly元素被用于对每个依赖的程序集指定版本和定位设置。



图2.10:程序集解析器配置文件的格式

示例2.4展示了一个简单配置文件,它包含程序集的两个版本策略。第一个策略将指定程序集(Acme.HealthCare)的1.2.3.4版重定向为1.3.0.0版。第二个策略将那个程序集的1.0.0.0版至1.2.3.399版重定向为1.2.3.7版。
示例2.4:设置版本策略

<?xml version="1.0" ?><configuration
xmlns:asm="urn:schemas-microsoft-com:asm.v1"

<runtime>
<asm:assemblyBinding>
<!-- one dependentAssembly per unique assembly name -->
<asm:dependentAssembly>
<asm:assemblyIdentity
name="Acme.HealthCare"
publicKeyToken="38218fe715288aac" />
<!-- one bindingRedirect per redirection -->
<asm:bindingRedirect oldVersion="1.2.3.4"
newVersion="1.3.0.0" />
<asm:bindingRedirect oldVersion="1-1.2.3.399"
newVersion="1.2.3.7" />
</asm:dependentAssembly>
</asm:assemblyBinding>
</runtime>
</configuration>

版本策略能在三个级别上被指定:按应用程序(per Application)、按组件(percomponent)以及按机器(per machine)。每个级别都有机会处理版本号,高级别的结果将充当低级别的输入,如图2.11所示。注意,对于给定的程序集,如果应用程序和机器的配置文件各有一个版本策略,那么,应用程序的策略优先执行,接着通过执行计算机范围策略获得实际的版本号,以此定位程序集。在这个例子中,如果计算机范围的配置文件将Acme.HealthCare的1.3.0.0版重定向为2.0.0.0版,而应用程序的版本策略把版本1.2.3.4映射为版本1.3.0.0,那么,当请求1.2.3.4版时,程序集解析器将使用版本2.0.0.0。

除了特定应用程序和计算机范围的配置文件之外,给定的程序集还能有发行者策略(publisher policy)。发行者策略是来自组件开发人员的声明,它标明了给定组件的哪些版本是互相兼容的。


图2.11:版本策略

发行者策略作为配置文件,存储在计算机范围的全局程序集缓存中。这些文件的结构与应用程序和计算机的配置文件的结构是完全相同的。然而,为了在用户机器上安装,发行者策略的配置文件必须与包含它的程序集DLL一起封装,作为一个定制的资源。假定文件foo.config 包含发行者的配置策略,下面的命令行将调用程序集链接程序(AL.EXE),为AcmeCorp.Code的2.0版创建一个适合的发行者策略的程序集:
al.exe /link:foo.config
/out:policy.2.0.AcmeCorp.Code.dll
/keyf:pubpriv.snk
/v:2.0.0.0
发行者策略文件的名字形式为policy.major.minor.assmname.dll。由于这个命名约定,给定程序集对每个major.minor版本只能有一个发行者策略文件。在这个例子中,对于major.minor版本号为2.0的AcmeCorp.Code.DLL而言,所有请求将通过与policy.2.0.AcmeCorp.Code.DLL链接的策略文件转发。如果全局程序集缓存[global assembly cache(GAC)]中不存在这样的程序集,那么,就没有发行者策略。如图2.11所示,发行者策略的应用是在特定应用程序的版本策略之后,而在machine.config中的计算机范围版本策略之前。

在版本化组件软件中, CLR允许程序员在应用程序范围的基础上取消发行者版本策略。为此,程序员在应用程序配置文件中使用publisherPolicy元素。示例2.5展示了一个简单配置文件中的publisherPolicy元素。当这个元素含有apply=“no”特性时,对于应用程序而言,发行者策略将被忽略;当这个特性被设置为apply=“yes”(或根本没有被限定)时,发行者策略将如刚才所描述的那样被使用。如图2.10所示,publisherPolicy元素能在应用程序范围或者多个程序集范围的基础上启用或取消发行者策略。
示例2.5:设置应用程序为安全模式

<configuration xmlns:rt="urn:schemas-microsoft-com:asm.v1">
<runtime>
<rt:assemblyBinding>
<rt:publisherPolicy apply="no" />
</rt:assemblyBinding>
</runtime>
</configuration>

来源:.net学习网
说明:所有来源为 .net学习网的文章均为原创,如有转载,请在转载处标注本页地址,谢谢!
【编辑:Wyf】

打赏

取消

感谢您的支持,我会做的更好!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

最新评论

共有评论0条
  • 暂无任何评论,请留下您对本文章的看法,共同参入讨论!
发表评论:
留言人:
内  容:
请输入问题 76+19=? 的结果(结果是:95)
结  果: