转载 应用程序域

应用程序域  

2012-09-18 14:06:22|  分类: .NET框架程序设计 |  标签:元数据   |举报 |字号 订阅

1.         元数据:.NET框架的基石

2.         CLR寄宿

3.         应用程序域

4.         反射概要

5.         反射一个程序集中类型

6.         反射一个应用程序域中的程序集

7.         反射一个类型的成员

8.         反射一个类型的接口

9.         反射的性能

应用程序域

         CLR COM服务器被加载到一个Windows进程中时,它便开始执行初始化。初始化的部分工作就是创建一个托管堆,该托管堆将用于分配所有的引用对象、以及对它们执行垃圾收集。另外,CLR还会创建一个可被加载到当前进程中所有托管类型使用的线程池。在进行这些初始化工作的同时,CLR还会创建一个应用程序域(AppDomain)。一个应用程序域是一组程序集的一个逻辑容器。CLR初始化时创建的第一个应用程序域称作默认应用程序域(default AppDomain),该应用程序域只有在Windows进程中断时才会被注销。

         注意:在第1版的.NET框架中,一个Windows进程中最多只能存在一个CLR COM服务器对象,并且只有在宿主进程中断时,该CLR COM服务器对象才会被销毁。也就是说,假如CorBindToRuntimeEx函数创建了一个CLR COM服务器,并返回一个指向ICorRuntimeHost接口指针,那么在该接口上调用AddRefRelease方法将没有任何效果。另外,由于一个宿主进程只能创建一个CLR COM服务器实例,所以如果一个宿主进程多次调用CorBindToRuntimeEx函数,那么每次返回的都将是同一个ICorRuntimeHost指针。

         除了默认的应用程序域外,一个宿主还可以指示CLR创建额外的应用程序域。另外,托管程序集中的代码也可以告诉CLR创建额外的应用程序域。应用程序域有三个非常有用的特点:

l  应用程序域之间是相互隔离的     一个应用程序域看不到另一个应用程序域中的对象。这种清晰的隔离是强制性的,因为一个应用程序域中代码不能直接引用另一个应用程序域中创建的对象。这种隔离策略使得应用程序域很容易从一个进程中被卸载下来。

l  应用程序域可以被卸载         CLR不支持单个程序集的卸载。但是,我们可以告诉CLR卸载一个应用程序域及其内容包含的所有程序集。

l  应用程序域可以单独实施安全策略和配置策略         当一个应用程序域被创建时,它将会与特定的证据(evidence)联系在一起。证据是一种与安全相关的特性(feature),它决定了运行在应用程序域中的程序集所享有的最大权利。通常我们可以使用AppDomainSetAppDomainPolicy方法来在应用程序域上应用某种安全策略。另外,System.AppDomainSetup类也允许我们设置或者查询一个应用程序域的配置策略。这些配置规定了CLR怎样定位和加载程序集,它们包括以下内容:

ApplicationName     一个用户友好的字符串名称,该名称用来标识一个应用程序域

ApplicationBase       一个目录,CLR使用该目录来定位程序集。

PrivateBinPath                   一个目录集合,CLR使用该目录集合来定位弱命名程序集。

ConfigurationFile     一个配置文件的路径名,该配置文件中包括了CLR用来定位程序集的规则以及远程(remoting)设置、Web应用程序设置等。

LoaderOptimization         一个标记,该标记用来告诉CLR将加载的程序集是以中立域(domain-neutral)、还是以独立域(single-domain)的方式来对待。

重要:在一个进程中运行多个非托管应用程序是很危险的,因为不同的应用程序将能访问彼此的数据和代码,从而使得一个应用程序可以很容易破坏另一个应用程序。但托管代码却不存在这样的问题,因为托管IL代码是类型安全的,并且也是经过验证的,这使得一个应用程序域中的代码不可能破坏另一个应用程序域中代码。当然,管理员可以关闭验证过程,从而允许托管代码调用非托管函数。如果管理员这么做,那么以上所有的担保将全部失效,应用程序域的崩溃也是完全有可能的。

下图演示了一个单独的Windows进程,其中运行有一个CLR COM服务器。该CLR管理着两个应程序域。每个应用程序域都有自己的加载器堆(loader heap),其中维护着自应用程序域创建以来被访问过的类型记录。加载器堆中的每个类型都有一个方法表,对于方法表中的每个条目,如果其中的方法至少执行过一次,那么它将指向被JIT编译后得到的x86代码。

应用程序域 - benlaishidoudou - 小布丁de读书笔记

 另外,每个应用程序域还有一些已经加载入其中的程序集。

AppDomain #1(默认的应用程序域)有三个程序集: MyApp.exeTypeLib.dllSystem.dllTypeLib.dll程序集包括三个模块: TypeLib.dll(其中包括着程序集清单)、 FUT.netmoduleRUT.netmoduleAppDomain #2中加载有三个单模块的程序集: Wintellect.dllSystem.dllMicrosoft.dll

默认情况下,一个程序集会被加载到每个应用程序域中。例如,AppDomain #1AppDomain #2中都加载了System.dll程序集。这意味着System.dll程序集的相关信息会在每个应用程序域的加载器堆中构建一次。甚至System.dll中类型定义的一些方法被JIT编译后的代码也会在该Windows进程的地址空间中存在两份。这样做的优点是一个应用程序域可以完全被从进程中卸载下来,而不影响其他的应用程序域。

但有时候我们也有一些程序集是期望被几个应用程序域所共享的。其典型例子就是MSCorLib.dll,这是微软创建的一个单模块程序集。该程序集中包括了System.Object以及所有其他对.NET框架来说所必须的类型。CLR初始化的时候,该程序集会被自动加载,所有的应用程序域会共享该程序集中的类型。为了减少资源使用,MSCorLib.dll程序集会以一种中立域的方式被加载。对于以中立域方式加载的程序集来说,CLR会为它们维护一个特殊的加载器堆。以中立域方式加载的程序集只有在进程中断时才会被卸载。

跨越应用程序域边界访问对象

                  一个应用程序域中的代码可以和另一个应用程序域中的类型和对象相互通信。但是,这样的通信必须通过一种预先定义的机制进行。大多数类型在跨越应用程序域边界时是通过传值的方式(by value)来进行封送处理(marshal)的。换句话说,如果我们在一个应用程序域中构造了一个对象,然后又将该对象的引用传递给了另一个应用程序域,那么CLR必须首先将该对象的字段序列化到一个内存块中,然后将该内存块传递给另一个应用程序域,最后再执行反序列化得到新的对象。

         目的应用程序域将使用这个新创建的对象引用,它不会访问原来应用程序域中的对象。对于以传值方式进行远程传送(remote)的对象来说,对象的类型必须应用System.Serializable定制特性。

         注意:如果需要的话,反序列化对象会导致CLR加载相关类型所在的程序集。如果CLR不能使用目的应用程序域的策略(例如该应用程序域可能有一个不同的AppBase目录)来定位这些程序集,对象将不能被反序列化,并且会抛出异常。

         除了应用System.Serializable定制特性的类型外,继承自System.MarshalByRefObject的类型也可以为对象提供跨越应用程序域边界的访问能力。但是,这样的访问是通过传引用(by reference)而非传值来进行的。假设我们在一个应用程序域中创建了一个对象(其类型继承自System.MarshalByRefObject)。当该对象的引用被传递给一个目的应用程序域时,CLR实际上会在目的应用程序域创建一个代理类型的实例,目的应用程序域中的代码将使用这个代理对象引用。原来的对象及其字段仍然驻留在原来的应用程序域中。代理对象实际上是一个封装器(wrapper),它知道怎么调用原来应用程序域中的对象上的实例方法。同样,目的应用程序域不会直接访问原来应用程序域中的对象。

         很明显,跨越应用程序域边界访问对象会有一些性能损耗。我们应该尽量避免这样的操作。

         线程和应用程序域之间不存在一对一的关系。当一个应用程序域中的线程调用另一个应用程序域中的方法时,线程会在两个应用程序域间跳转。这意味着跨越应用程序域边界的方法调用会被同步地执行。但是,在任何一个给定的时刻,一个线程都被认为只存在于一个应用程序域中。我们可以调用System.Threading.Thread的静态方法GetDomain来获得当前执行线程所在的应用程序域(由一个System.AppDomain对象所标识)。

         当一个应用程序域被卸载时,CLR会知道哪些线程位于该应用程序域中,并在这些线程中强制产生一个ThreadAbortException异常,使它们退出该应用程序域。一旦这些线程离开该应用程序域,CLR会使所有指向要卸载的应用程序域中的对象的那些代理对象变得无效。在这之后,所有对无效代理对象的方法调用都将导致一个System.AppDomainUnloadException异常被抛出,因为原来的对象已经不存在了。

      应用程序域事件

         下表简要描述了一个应用程序域提供的几个非常有用的事件:

事件名称

描述

AssemblyLoad

该事件在每次CLR将一个程序集加载到应用程序域中时被触发。事件处理器接受一个标识被加载程序集的System.Reflection.Assembly对象

DomainUnload

该事件在应用程序卸载之前被触发。如果包含应用程序域的进程发生中断,该事件将不会被触发

ProcessExit

该事件在进程中断之前被触发。该事件只会为默认的应用程序域触发,任何其他登记该事件的应用程序域将不会接到事件通知

UnhandleException

该事件在一个应用程序域中出现未处理异常时被触发

AssemblyResolve

该事件在CLR不能定位应用程序域所需要的程序集时被触发。事件处理器接受一个标识缺失程序集名称的字符串

ResourceResolve

该事件在CLR不能定位应用程序域所需要的资源时被触发。事件处理器接受一个标识缺失资源名称的字符串

TypeResolve

该事件在CLR不能定位应用程序域中某个程序集所需要的类型时被触发。事件处理器接受一个标识缺失类型名称的字符串,并且可以通过返回一个Type对象引用来告诉CLR要使用的类型。通常,处理器会根据客户端的位置或者操作系统来确定返回什么样的Type对象

 

应用程序及其如何寄宿CLR和管理应用程序域

         到目前为止,已经讨论了CLR宿主以及它们加载CLR和应用程序域的方式,另外还探讨了宿主如何告诉CLR来创建和卸载应用程序域。为了更具体地说明问题,下面描述一些常见的CLR寄宿和应用程序域的应用场景。大家将会了解到不同类型的应用程序是如何寄宿CLR以及管理应用程序域的。

控制台应用程序和Windows窗体应用程序

         当我们调用一个托管控制台应用程序、或者Windows窗体应用程序时,被称作“垫片”的MSCorEE.dll会检查应用程序的程序集中包含的CLR表头信息。该表头信息指示了生成与测试应用程序时所使用的CLR版本。MSCorEE.dll使用该信息来判断创建哪个CLR COM服务器。在CLR加载并初始化之后,它会再次检查程序集的CLR表头来判断应用程序的入口点方法(Main)。接着,CLR调用该方法,应用程序便开始运行。

         随着代码的运行,它会访问其他类型。当引用到包含在其他程序集中的类型时,CLR会定位必要的程序集、并将其加载到同一个应用程序域中。任何另外间接被引用的程序集也会被加载到同一个应用程序域中。当应用程序域的Main方法返回时,默认的应用程序域将被卸载,Windows进程也随之中断。

         注意:如果我们希望关闭Windows进程(包括其中所有的应用程序域),我们可以调用System.Environment的静态方法ExitExit是中断一个进程的最佳方式,因为它首先会调用托管堆中所有对象的Finalize方法,然后还会释放所有由CLR维护的非托管COM对象。最后,Exit将调用Win32函数ExitProcess

         除了默认的应用程序域外,控制台应用程序或Windows窗体应用程序还可以告诉CLR在进程的地址空间上创建额外的应用程序域。但是,这两类应用程序很少使用或者需要多个应用程序域。

ASP.NET Web窗体和XML Web服务应用程序

         ASP.NET是一个ISAPI DLL(实现于ASPNet_ISAPI.dll之中)。当客户端请求一个由ASP.NET ISAPI DLL处理的URL时,ASP.NET会创建一个工作者进程(worker process)(ASPNet_wp.exe)。工作者进程是一个寄宿有CLR COM服务器的Windows进程。

         当客户端向一个Web应用程序发出一个请求时,ASP.NET会判断该请求是否为第一次。如果是,ASP.NET会告诉CLR为该Web应用程序创建一个新的应用程序域(每个Web应用程序可以由它的虚拟目录来判定)。ASP.NET然后告诉CLR将必要的程序集(其中包含着组成该Web应用程序的类型)加载到新建的应用程序域中,并创建一个Web应用程序类型的实例,调用其中的方法响应客户端请求。随着Web应用程序的运行,如果代码中引用到了更多的类型,CLR会继续将必要的程序集加载到当前Web应用程序的应用程序域中。随便提一句,强命名程序集(例如System.Web.dll)是以中立域的方式来加载,因为这样做可以节省操作系统的资源。

         当更多的客户端向一个已经运行的Web应用程序发出请求时,ASP.NET不会再去创建新的应用程序域,相反,它会使用现有的应用程序域,创建一个新的Web应用程序类型的实例,并调用其中的方法。这些方法应该已经被JIT编译成了本地代码,所以后续客户端请求的处理性能将会有所提高。

         如果客户端请的是另外一个不同的Web应用程序,ASP.NET将会告诉CLR创建一个新的应用程序域。这个新创建的应用程序域典型地和其他的应用程序域处于同一个工作者进程中。这意味着许多Web应用程序将运行在同一个Windows继承中,这样有助于提高整个系统的效率。当然,不同的Web应用程序所需的程序集会被加载到自己的应用程序域中。

Microsoft Internet Explorer

         当我们安装.NET框架时,它会安装一个MIME筛选器(MSCorIE.dll)挂接在5.01版本及其以上的IE浏览器内。该MIME筛选器会处理标识有MIME类型为“application/octet-stream”或“application/x-msdownload”的下载内容。当该MIME筛选器检测到一个托管程序集被下载时会调用CorBindToRuntimeEx函数来创建一个CLR COM服务器。这使得IE浏览器的进程也称为CLR宿主。

         MIME筛选器由CLR控制,它可以确保来自同一个Web站点的所有程序集都被加载到属于自己的应用程序域中。这使得计算机管理员可以以不同的方式来对待来自不同站点的程序集(例如信任来自A站点的程序集,而不信任来自B站点的程序集)。这还使得IE浏览器可以在用户浏览不同Web应用程序时卸载前一个Web应用程序所使用的程序集。

Yukon

         SQL Server的下一个版本(代号Yukon)仍是一个非托管应用程序,因为它的大多数代码仍然采用非托管C++开发。但是,Yukon会在初始化时创建一个CLR COM服务器。这使得我们可以在Yukon中用托管语言(C#等)来编写存储过程。

         由于这些存储过程运行在应用了特定证据(evidence)的应用程序域中,因此便可以保证它们不会恶意损坏数据库服务器。当执行这些托管语言编写的存储过程时,Yukon将告诉CLR加载必要的程序集并调用其中的方法,根据相关的证据,这些方法将在一定的安全限制下运行。

         这种功能相当厉害!这意味着我们将可以用自己喜欢的语言编写存储过程,我们将可以在存储过程代码中使用强类型数据对象,而且编写的存储过程代码将以JIT编译的方式执行(而非目前的解释执行)。最后,我们还可以在编写存储过程的时候求助于.NET框架类库(FCL)或者其他程序集中定义的任何类型。显然这极大地减轻我们编写存储过程的工作,而且最后得到的应用程序性能也会更高。

         随着时间的推移,像字处理、电子表格这样的软件产品也将会允许我们选择自己喜欢的语言来编写宏( macro)。这些宏也将可以访问所有 CLR支持的程序集和类型。它们同样将以 JIT编译的方式执行,效率自然也会更高。更重要的是,这些宏将运行在一个安全的应用程序域中,因此也可以避免一些恶意攻击。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值