操作系统和运行库环境通常会在应用程序间提供某种形式的隔离。例如,Microsoft Windows 使用进程来隔离应用程序。为确保在一个应用程序中运行的代码不会对其他不相关的应用程序产生不良影响,这种隔离是必需的。
应用程序域为安全性、可靠性、版本控制以及卸载程序集提供了隔离边界。应用程序域通常由运行库宿主创建,运行库宿主负责在运行应用程序之前引导公共语言运行库。
二、 如何进入“托管”世界
首先,我们要了解,到目前为止,还没有“纯天然的”.net执行环境(不排除类似Java芯片的.net芯片将来会有),所谓托管的环境(CLR)需要运行在当前已存非托管的系统上。要进入托管的.net世界,需要有一个称为宿主(Host)的程序为将要运行的.net托管代码准备执行环境—也就是要加载.net世界的基础CLR。在目前的windows系统上,能够担负这个重任的有3类已存程序:
1、 shell(通常是Explorer),提供从用户桌面启动.net程序,创建一个进程,启动此进程建立CLR
2、 浏览器宿主(Internet Explorer),处理从web下载的.net代码执行。
3、 服务器宿主(如IIS的辅助进程aspnet_wp.exe)
在执行任何托管代码之前,宿主必须首先加载并初始化公共语言运行库。假设一个.net可执行程序(prj1.exe)从shell启动,操作系统会首先建立一个进程,也就是宿主进程。装载的程序文件包括了在执行配置信息和执行代码,代码入口通常会被(创建此.net应用程序的编译器)放置一个 Stub,这个Stub实际上就是一个6字节的本机代码:
jmp _CorExeMain
而_CorExeMain是从外部库MSCorEE.dll导出,由prj1.exe引入的函数,于是操作系统会装入MSCorEE.dll(进入.net世界的序曲),修正_CorExeMain的运形时实际位置。
MSCorEE.dll实际上是一个COM组件库。调用_CorExeMain后开始初始化CLR,并察看prj1.exe的CLR相关数据结构,确定执行.net托管代码的入口。宿主调用.net 支持API CorBindToRuntimeEx来装载CLR,并且根据配置初始化CLR的运行特征,譬如垃圾回收策略等,这是因为不同的宿主面临的应用需求不一样,一个服务宿主同普通的工作站宿主的“垃圾回收”(GC)机制显然不一样,所以启动CLR时的参数也不一样。
宿主装载的是一个符合COM规范要求的组件库文件MSCorEE.dll,也就是CLR,一般存在于操作系统目录(便于装载)。有关mscoree.dll更多的了解,建议可看看FrameworkSDK目录中的头文件mscoree.h。MSCorEE.dll通过提供启动CLR的接口给宿主,譬如ICorRuntimeHost接口可用来配置运行库的各个方面(如垃圾回收),以将其加载到进程中或注册附加的事件,其中的start/stop方法可以让宿主控制CLR在宿主进程的生存期。
其实我们可以利用CorBindToRuntimeEx编写实现自己的宿主,关于.net宿主的实现可以单独作为一个题目,在此给出几个URL供大家参考。
http://www.codeproject.com/dotnet/simpleclrhost.asp
http://www.elitevb.com/content/print.aspx?contentid=95
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/grfuncorbindtoruntimeex.asp
至此,我们明白, CLR——无所不能的虚拟机以DLL形式“寄生”于某个非托管世界的进程!所谓托管世界实际上实指在Mscoree.dll建立的可控制环境下。难怪精通VCL的用户说CLR就是MS版的VCL
三、 应用程序域(AppDomain)
应用程序域是.net 托管世界中的“应用程序在其中执行的独立环境”,是要执行或引用的多个程序集的容器(一个应用程序域肯定不止加载一个程序集)。千万不要理解成进程的概念,应用程序域存在于CLR中,而CLR属于宿主进程,应用程序域同进程属于不同层次上的概念。但的确,.net的设计者是仿照操作系统的进程概念来设计应用程序域的,使得AppDomain成为.net世界的执行单位,相互之间代码执行隔绝。大体上,下图可以帮助理解宿主、CLR、应用程序域之间的关系。应用程序域是.net 托管世界中的“应用程序在其中执行的独立环境”,是要执行或引用的多个程序集的容器(一个应用程序域肯定不止加载一个程序集)。千万不要理解成进程的概念,应用程序域存在于CLR中,而CLR属于宿主进程,应用程序域同进程属于不同层次上的概念。但的确,.net的设计者是仿照操作系统的进程概念来设计应用程序域的,使得AppDomain成为.net世界的执行单位,相互之间代码执行隔绝。大体上,下图可以帮助理解宿主、CLR、应用程序域之间的关系。
应用程序域是.net 托管世界中的“应用程序在其中执行的独立环境”,是要执行或引用的多个程序集的容器(一个应用程序域肯定不止加载一个程序集)。千万不要理解成进程的概念,应用程序域存在于CLR中,而CLR属于宿主进程,应用程序域同进程属于不同层次上的概念。但的确,.net的设计者是仿照操作系统的进程概念来设计应用程序域的,使得AppDomain成为.net世界的执行单位,相互之间代码执行隔绝。大体上,下图可以帮助理解宿主、CLR、应用程序域之间的关系。
四、 AppDomain小结
实际上我们看到,.net的托管环境CLR是通过将COM DLL文件msCorEE.dll装入当前操作系统进程来建立的。托管世界的执行对象提供元数据和IL代码以及安全证据等在CLR的内存对执行,所有的托管代码经过Jit编译成本地代码,由于CLR的一切对象被“托管”可以确保AppDomain的实现类似进程环境的执行分隔作用,CLR可以根据应用程序域的边界阻止任何不安全的访问。CLR也跟踪管理托管线程,线程可以通过域间的通信功能实现线程在多个应用程序域上的移动。进程和线程属于操作系统的调度执行单元,但应用程序域属于.net的执行逻辑单元,通过“托管”实现应用程序域的代码执行以及域间数据访问的分隔。
通常情况下,我们的.net应用程序仅仅需要一个缺省的应用程序域,但也有一些情况考虑建立其他应用程序域可能更好一些:
1、 需要隔离的程序集,譬如一些特别容易引起崩溃的代码可以考虑单独运行于一个特定的AppDomain
2、 不同安全级别的程序集,如果需要为自己的代码划分安全执行的边界,可以考虑将不同安全级别的代码单独创建于某个设定了不同安全信息的appDomain
3、 从性能上考虑,有些程序集可能会消耗大量资源,尽管在托管环境下,基本上不存在资源消耗漏洞,但是总会存在特定时间访问密集造成消耗大量资源的情况,这时可以考虑创建单独的AppDomain,在资源消耗超过临界点后进行AppDomain的卸载,适应系统运行要求。
4、 不同版本的同一应用程序集的同时运行。这个在COM时代是一个大问题,现在通过AppDomain,实现了在一个进程中执行版本不同的两个程序集,可以做到良好的兼容性。
5、动态加载一些程序。可以将一些不经常使用的程序集动态载入,(为了效率)经常使用的程序集则可以动态加载,甚至单独加载到应用程序域中去。
6、共用程序集,提高.net的执行效率。譬如我们用到的System.object System.Int32 等对象往往多个应用程序域都需要,会造成资源浪费,为减少资源使用,含有这些常用.net类的程序集MSCorLib.dll会以单独的中立域的方式加载,CLR会为其维护一个特殊的加载器,使得这个程序集只有在进程中断时才会被卸载,从而提高了速度,减少了内存等资源的浪费。
五、 参考
http://study.pay500.com/3/s38501.htm