要提高代码的可靠性,就必须实现某种代码隔离层(或者说独立层)。隔离层要确保运行在某个隔离边层内的代码不会对其他隔离层中的代码产生负面影响。 如果不能实现这种保证,那么一些不良行为的代码将很容易破坏其他的应用程序或者整个系统。换句话说,这种隔离层的作用就是提升系统的稳定性与可靠性。 Windows通过进程来实现这种隔离机制。所有的可执行代码、数据,以及其他资源都被包含在进程中,系统中其他进程通常不允许对它们进行访问(除非持有 足够的权限)。同理,.NET应用程序同样是被局限在进程内执行。但是,.NET还进一步引入了另一种逻辑隔离层,称之为应用程序域。在图2-4中给出了 进程与应用域之间的关系。
(点击查看大图)图2-4进程与应用程序域 |
在任何启动了CLR的Windows进程中都会定义一个或多个应用程序域,在这些域中包含了可执行代码、数据、元数据结构以及资源等。除了进程本身带有的保护机制外,应用程序域还进一步引入了以下保护机制:
在某个应用程序中的错误代码不会影响到同一进程中另一个应用程序域中运行的代码。
运行在某个应用程序域中的代码不能直接访问另一个应用程序域中的资源。
在每个应用程序域中都可以配置与代码特定的信息。例如,可以在每个应用程序域中配置不同的安全设置。
通常,应用程序域对于应用程序本身来说是透明的,大多数应用程序都不会显式地创建任何应用程序域。只有那些需要在同一个进程中运行特定代码,并要求 实现某种程度隔离性的应用程序才会创建新的应用程序域。为了确保运行的代码不会对系统的其他部分造成破坏,这些代码将被加载到自己的应用程序域中。例 如,Internet信息服务器(Internet Information Server,IIS)可以支持多个ASP.NET页面,我们可以将运行环境配置为:每个页面在同一进程的不同应用程序域中运行。对于没有显式创建应用程 序域的应用程序来说,CLR(在加载的时候)将创建三个应用程序域:系统应用程序域,共享应用程序域以及默认应用程序域。换句话说,启动了CLR的进程在 运行时至少拥有三个应用程序域(尽管程序本身并没有显式地创建任何应用程序域)。我们可以通过非托管调试器来查看当前进程中存在哪些应用程序域。首先在调 试器下启动.NET应用程序02simple.exe,然后执行SOS调试器扩展支持的dumpdomain命令,如清单2-3所示。
清单2-3通过dumpdomain命令来显示02simple.exe中的所有应用程序域
从清单2-3中可以看到,在这个进程中有三个应用程序域:System、Shared、Domain 1。其中Domain 1是默认的应用程序域,它的名字就是映像本身的名字(02simple.exe)。在每个应用程序域的输出信息中包含以下内容:
指向应用程序域的指针。这个指针可以作为dumpdomain 命令的输入参数,这样将只输出指定应用程序域的信息。例如,可以执行以下命令:
这个命令只会输出系统应用程序域的信息。
LowFrequencyHeap,HighFrequencyHeap以及StubHeap。通常,每个应用程序域都有与之相关的MSIL代码。 在JIT编译MSIL的过程中,JIT编译器需?4嬗氡嘁牍滔喙氐氖荨@纾嘁肷傻幕鞔牒头椒ū淼取R虼耍扛鲇τ贸绦蛴蚨夹枰唇ㄒ欢ㄊ 康亩牙创娲⒄庑┦荨T贚owFrequencyHeap中包含的是一些较少被更新或者被访问的数据,而在HighFrequencyHeap中包含的则 是被频繁访问的数据。最后一个堆是StubHeap,在这个堆中包含的是CLR执行互用性调用(例如COM互用性或者平台调用)时需要的辅助数据。
在应用程序域中加载的所有程序集。从清单2-3中可以看出,02simple.exe这个应用域加载了两个程序集:mscorlib.dll和 02simple.exe。除了给出已加载.NET程序集的版本外,还给出了底层程序集数据结构的地址。例如,02simple.exe程序集的地址为 0x004647d0。在本章的稍后部分将用到这个程序集地址。
我们可以看到,非托管调试器有助于收集.NET进程中与隔离边界(即应用程序域)相关的信息。最后需要讨论的是这三个应用程序域的作用。记住,即 使.NET应用程序没有显式地创建任何应用程序域,仍然会存在三个应用程序域:系统、共享以及默认。接下来将分别介绍这三个应用程序域的作用。
为什么不使用多个进程而使用多个应用程序域