什么叫应用程序域(桌面应用程序是什么)
为了提高系统的稳定性和可靠性,必须确保在一个应用程序中运行的代码不会对其他不相关的应用程序产生不良影响。在window操作系统中,不同的应用程序通过进程实现了隔离机制。所有的可执行代码、数据,以及其他资源都被包含在进程中,其他进行通常不允许对它们进行访问(除非拥有足够的权限)。在.NET应用程序中也是在进程内执行的。 除此之外,.NET还引用的一种代码隔离机制,称之为应用程序域。应用程序域又分为:系统应用程序域、共享应用程序域和默认应用程序域。图1表示了进程与应用程序域之间的关系。
除了进行本身带有的保护机制外,应用程序域还引入了以下保护机制:
-
在某个应用程序域中的错误代码不会影响到同一进程中另一个应用程序域中运行的代码。
-
运行在某个应用程序域中的代码不能直接访问另一个应用程序域中的资源。
-
在每个应用程序域中都可以配置与代码特定的信息。例如,可以在每个应用程序域中配置不同的安全设置。
通常情况下,应用程序域对于应用程序本身来说是透明的,大多数应用程序都不会显示的创建任何应用程序域。CLR在加载程序中会自动创建三个应用程序域:系统应用程序域、共享应用程序域和默认应用程序域。所有CLR启动的进程中至少包含三个应用程序域。
举例说明
-
用visual studio创建一个console程序,项目名称为“ConsoleApp1”代码如下:
class Program{ static void Main(string[] args) { Console.WriteLine("程序结束"); Console.ReadLine(); } }
-
生成解决方案,在项目中debug目录中启动ConsoleAPP1.exe程序。
-
用windbg加载consoleApp1.exe程序,如下图所示:
windbg加载项如下:
Microsoft (R) Windows Debugger Version 10.0.18362.1 AMD64Copyright (c) Microsoft Corporation. All rights reserved. *** wait with pending attachSymbol search path is: srv*Executable search path is: ModLoad: 000001f0`0fc50000 000001f0`0fc56000 D:\practice\ConsoleApp1\ConsoleApp1\bin\x64\Debug\ConsoleApp1.exeModLoad: 00007fff`bed00000 00007fff`beef0000 C:\WINDOWS\SYSTEM32\ntdll.dllModLoad: 00007fff`a85a0000 00007fff`a8604000 C:\WINDOWS\SYSTEM32\MSCOREE.DLLModLoad: 00007fff`beb80000 00007fff`bec32000 C:\WINDOWS\System32\KERNEL32.dllModLoad: 00007fff`bca80000 00007fff`bcd23000 C:\WINDOWS\System32\KERNELBASE.dllModLoad: 00007fff`bdd20000 00007fff`bddc3000 C:\WINDOWS\System32\ADVAPI32.dllModLoad: 00007fff`bd210000 00007fff`bd2ae000 C:\WINDOWS\System32\msvcrt.dllModLoad: 00007fff`be920000 00007fff`be9b7000 C:\WINDOWS\System32\sechost.dllModLoad: 00007fff`bda30000 00007fff`bdb50000 C:\WINDOWS\System32\RPCRT4.dllModLoad: 00007fff`a8390000 00007fff`a8439000 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscoreei.dllModLoad: 00007fff`bd2c0000 00007fff`bd312000 C:\WINDOWS\System32\SHLWAPI.dllModLoad: 00007fff`be5e0000 00007fff`be916000 C:\WINDOWS\System32\combase.dllModLoad: 00007fff`bc610000 00007fff`bc70a000 C:\WINDOWS\System32\ucrtbase.dllModLoad: 00007fff`bc8b0000 00007fff`bc930000 C:\WINDOWS\System32\bcryptPrimitives.dllModLoad: 00007fff`bce60000 00007fff`bce86000 C:\WINDOWS\System32\GDI32.dllModLoad: 00007fff`bc7e0000 00007fff`bc801000 C:\WINDOWS\System32\win32u.dllModLoad: 00007fff`bc470000 00007fff`bc604000 C:\WINDOWS\System32\gdi32full.dllModLoad: 00007fff`bc810000 00007fff`bc8ae000 C:\WINDOWS\System32\msvcp_win.dllModLoad: 00007fff`be440000 00007fff`be5d4000 C:\WINDOWS\System32\USER32.dllModLoad: 00007fff`bdc20000 00007fff`bdc4e000 C:\WINDOWS\System32\IMM32.DLLModLoad: 00007fff`bbbd0000 00007fff`bbbe1000 C:\WINDOWS\System32\kernel.appcore.dllModLoad: 00007fff`b15c0000 00007fff`b15ca000 C:\WINDOWS\SYSTEM32\VERSION.dllModLoad: 00007fff`a6910000 00007fff`a73d1000 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dllModLoad: 00007fff`a6570000 00007fff`a6586000 C:\WINDOWS\SYSTEM32\VCRUNTIME140_CLR0400.dllModLoad: 00007fff`a6310000 00007fff`a63cd000 C:\WINDOWS\SYSTEM32\ucrtbase_clr0400.dllModLoad: 00007fff`bda10000 00007fff`bda18000 C:\WINDOWS\System32\psapi.dllModLoad: 00007fff`a18a0000 00007fff`a2ea0000 C:\WINDOWS\assembly\NativeImages_v4.0.30319_64\mscorlib\cea8b8fbc469dcbc6224d523a578e4b3\mscorlib.ni.dllModLoad: 00007fff`bea20000 00007fff`beb77000 C:\WINDOWS\System32\ole32.dllModLoad: 00007fff`a12b0000 00007fff`a13fe000 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clrjit.dll(2fc0.2b68): Break instruction exception - code 80000003 (first chance)ntdll!DbgBreakPoint: 00007fff`bed9faf0 cc int 3
-
执行SOS调试器扩展支持的dumpdomain命令
0:006> .loadby sos clr0:006> !dumpdomain c0000005 Exception in C:\Windows\Microsoft.NET\Framework64\v4.0.30319\sos.dumpdomain debugger extension. PC: 00007fff`61b7a3a1 VA: 00000000`00000000 R/W: 0 Parameter: 00000000`000000000:006> !dumpdomain -------------------------------------- System Domain: 00007fffa732d7d0LowFrequencyHeap: 00007fffa732dd48HighFrequencyHeap: 00007fffa732ddd8StubHeap: 00007fffa732de68Stage: OPENName: None -------------------------------------- Shared Domain: 00007fffa732d200LowFrequencyHeap: 00007fffa732dd48HighFrequencyHeap: 00007fffa732ddd8StubHeap: 00007fffa732de68Stage: OPENName: NoneAssembly: 000001f00fe2f7a0 [C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]ClassLoader: 000001f00fe2f2f0 Module Name00007fffa18a1000 C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll -------------------------------------- Domain 1: 000001f00fdc5f20LowFrequencyHeap: 000001f00fdc6718HighFrequencyHeap: 000001f00fdc67a8StubHeap: 000001f00fdc6838Stage: OPENSecurityDescriptor: 000001f00fd9d150Name: ConsoleApp1.exeAssembly: 000001f00fe2f7a0 [C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]ClassLoader: 000001f00fe2f2f0SecurityDescriptor: 000001f00fe2f6b0 Module Name00007fffa18a1000 C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dllAssembly: 000001f00fe3d6b0 [D:\practice\ConsoleApp1\ConsoleApp1\bin\x64\Debug\ConsoleApp1.exe]ClassLoader: 000001f00fe36a70SecurityDescriptor: 000001f00fe3d5c0 Module Name00007fff472c4148 D:\practice\ConsoleApp1\ConsoleApp1\bin\x64\Debug\ConsoleApp1.exe
在这个进程中有三个应用程序域:System、Shared、Domain1。其中Domain 1就是默认应用程序域。在每个应用程序域的输出信息中包含以下内容:
-
指向应用程序域的指针。这个指针可以作为dumpdomain命令的输入参数,这样将只输出指定应用程序域的信息。
-
每个应用程序都需要创建一定数量的堆来存储JIT编译生成的机器代码和方法表等数据。在LowFrequencyHeap中包含的是一些较少被更新或者被访问的数据,而HighFrequencyHeap中包含的则是被频繁访问的数据,StupHeap包含的是CLR执行互用性(例如COM互用性或者平台调用)调用时需要的辅助数据。
-
在应用程序域中加载的所有程序集。本例中加载了两个程序集:mscorlib.dll和ConsoleApp1.exe。除此之外还给出了底层程序集数据结构的地址。例如ConsoleApp1.exe程序集的地址为000001f00fe3d6b0。
作用
系统应用程序域
系统应用程序域实现的主要功能如下:
-
创建其他两个应用程序域(共享应用程序域和默认应用程序域)。
-
将mscorlib.dll加载到共享应用程序域中。
-
记录进程中所有其他的应用程序域,包括提供加载/卸载应用程序域等功能。
-
记录字符串池中的字符串常量,因此允许任意字符串在每个进程中都存在一个副本。
-
初始化特定类型的异常,例如内存耗尽异常,栈溢出异常以及执行引擎异常等。
共享应用程序域
在共享应用程序域中包含的是与应用程序域无关的代码。mscorlib.dll将被加载到这个应用程序域中(由系统应用程序域负责加载),此外还包括在System命名空间中的一些基本类型(例如String、enum、ValueType、Array等)。在大多数情况下,非用户代码(non-usercode)将被加载到共享应用程序域中,不过也有一些机制可以将用户代码(user code)加载到共享应用程序域中。启用了CLR的应用程序域可以通过加载器的优化属性来注入用户代码。
默认应用程序域
通常,.NET程序在默认应用程序域中运行。位于默认应用程序域中的所有代码都只有在这个域中才是有效的。由于应用程序域实现了一种有逻辑并且可靠的边界,因此任何跨越应用程序域的访问操作都必须通过.NET远程对象来进行。
在处理某些特定的问题时,如果能够理解应用程序域的本质,知道哪些应用程序域会被自动创建,以及应用程序域是可以被动态创建的,那么能给问题分析带来巨大的帮助。在第二部分调试实践中,我们将看到一些由于错误使用应用程序域而导致的问题。
答疑
在讨论应用程序域时经常有人提问:为什么不使用多个进程而使用多大个应用程序域。答案是,构建和管理进程的开销是非常高的。如果使用应用程序域这种逻辑概念,那么将极大地降低在创建与销毁隔离层时需要的开销。
总结
到目前为止,我们已经介绍了.NET应用程序的加载过程,隔离边界(也就是应用程序域),以及运行不同代码所在的位置,因此接下来就可以观察在程序集中包含的内容以及CLR与程序集之间的交互。