分类

  • 软件天地

  • 谈 WinDbg 之 AppDomain 的创建过程‖
    AppDomain 是介于操作系统
    使用者可以通过 AppDomain
    单还是蛋生鸡的问题,这个
    AppDomain 类型的 AppDoma
    AppDomain.CreateDomain
    mbly 是在名为 AppDomain 的逻
    层面进程和线程概念之间,同时
    .CreateDomain 创建新的 AppDo
    AppDomain.CreateDomain 方法
    in 里面被调用的,但这个 AppD
    方法创建的呢?呵呵
    辑空间中被载入运行的,而
    具有线程的轻便和进程的封闭性,
    main。这样一来就出现了一个鸡生
    肯定是要在一个载入了
    omain 又是谁调用

      我们可以使用 WinDbg
    当前运行的 AppDomain 情
    + SOS 的 EEHeap 命令ü?br>况。我们以下面这段代码为例
    出 CLR 执行引擎的堆信息,获取

      以下内容为程序代码:                                                          

      //                                                                          
      // AppDomain.cs                                                
      //                                                                          
      using System;                                                    

      public class EntryPoint                                
      {                                                                            
      public static void Main(string[] args)  
      {                                                                            
      Console.Out.WriteLin
    e("Hello AppDomain!"[img]/im
    ages/wink.gif[/img];
      Console.In.ReadLine();                                  
      }                                                                            
      }                                                                            

      这个典型的 CLR 程序的输出如下:                                          

      以下为引用:                                                                  

      0:003> !EEHeap

      succeeded                                                            
      Loaded Son of Strike data table
    SMicrosoft.NETFrameworkv1.1.4322msco
    version 5 from "E:WINDOW
    rwks.dll"
      Loader Heap:                                                      
      --------------------------------------  
      System Domain: 793e6fc8                                
      LowFrequencyHeap:00960000(2000:0
    0001000)
      Size: 0x00001000(4096) bytes.                    

      HighFrequencyHeap:00
    962000(8000:00001000)
      Size: 0x00001000(4096) bytes.                    
      StubHeap:0096a000(2000:00001000)              
      Size: 0x00001000(4096) bytes.                    
      Total size: 0x3000(12288)bytes                  
      --------------------------------------  
      Shared Domain: 793e83f8                                
      LowFrequencyHeap:00990000(2000)
    06c40000(10000:00007000)
      Size: 0x00009000(36864) bytes.                  

      HighFrequencyHeap:00
    992000(8000:00001000)
      Size: 0x00001000(4096) bytes.                    
      StubHeap:0099a000(2000:00001000)              
      Size: 0x00001000(4096) bytes.                    
      Total size: 0xb000(45056)bytes                  
      --------------------------------------  
      Domain 0: 147330                                              
      LowFrequencyHeap:009
    70000(2000) 06c60000(10000:0
    0004000)
      Size: 0x00006000(24576) bytes.                  

      HighFrequencyHeap:00
    972000(8000:00004000)
      Size: 0x00004000(16384) bytes.                  
      StubHeap:0097a000(2000:00001000)              
      Size: 0x00001000(4096) bytes.                    
      Total size: 0xb000(45056)bytes                  
      --------------------------------------  
      Jit code heap:                                                  
      Normal Jit:06c80000(10000:00002000)        
      Size: 0x00002000(8192) bytes.                    
      Total size 0x00002000(8192)bytes.            
      --------------------------------------  
      Total LoaderHeap size: 0x1b000(1
    10592)bytes
      =======================================
      generation 0 starts at 0x04aa1040            
      generation 1 starts at 0x04aa1034            
      generation 2 starts at 0x04aa1028            
      segment  begin allocated   size                
      04aa0000 04aa1028 04aa4000 0000
    2fd8(12248)
      Large object heap starts at 0x05aa1028  
      segment  begin allocated   size                

      05aa0000 05aa1028 0
    5aa6000 0x00004fd8(20440)
      Total Size  0x7fb0(32688)                            
      ------------------------------                  
      GC Heap Size  0x7fb0(32688)                        

      我们可以看到,虽然这
    已经有了三个 AppDomain:
    用 DumpDomain 命令查看三
    个程序非常简单,没有自己创建
    "System Domain", "Shared Dom
    个 AppDomain:
    任何 AppDomain,但实际上 CLR
    ain" 和 "Domain 0"。而进一步使


      以下为引用:                                                                  

      0:003> !DumpDomain 793e6fc8

      Domain: 793e6fc8                                              
      LowFrequencyHeap: 793e702c                          
      HighFrequencyHeap: 793e7080                        
      StubHeap: 793e70d4                                          
      Name:                                                                    
      Assembly: 00158e48 [mscorlib]                    
      ClassLoader: 00158f20                                    
      Module Name                                                        
      79b66000 e:windowsmicrosoft.net
    rameworkv1.1.4322mscorlib.dll

      0:003> !DumpDomain 793e83f8

      Domain: 793e83f8                                              
      LowFrequencyHeap: 793e845c                          
      HighFrequencyHeap: 793e84b0                        
      StubHeap: 793e8504                                          
      Name:                                                                    

      0:003> !DumpDomain 147330

      Domain: 00147330                                              
      LowFrequencyHeap: 00147394                          
      HighFrequencyHeap: 001473e8                        
      StubHeap: 0014743c                                          
      Name: appdomain.exe                                        
      Assembly: 0015c2c0 [appdomain]                  
      ClassLoader: 00161008                                    
      Module Name                                                        
      00161d50 d: empappdomain.exe                      

      我们可以看到,System
    的;Shared Domain 暂时没
    以猜测 System Domain 是
    mscorlib 创建其他 AppDom
    代码,是否能够予以印证。
    Domain 实际上是专门用于载入
    有使用;而 Domain 0 则负责运
    CLR 专门用来载入系统基础库的
    ain 以运行用户目标 Assembly

    mscorlib.dll 这个 BCL 基础库
    行我们的目标 Assembly。我们可
    ,而系统将进一步使用此
    。我们接下来看看 Rotor 的相关

      在 CLR 启动时负责加
    此函数首先在进行基础性初
    SystemDomain,然后加载并
    SystemDomain::Init 函数
    载执行引擎的 EEStartup 函数(
    始化工作后,调用 SystemDomai
    初始化异常处理、JITer等等支
    完成初始化 SystemDomain 等等
    vmceemain.cpp:206)中,可以发现
    n::Attach 函数载入
    持代码,最后会调用
    工作。
      SystemDomain::Attach 函数(vmappd
    stub 管理器和 SystemDomain 的静态成
    的内存区,构造并初始化 SystemDomain
    中,用于以后判断 SystemDomain 是否被
    SharedDomain。函数的简要功能代码如下
    omain.cpp:912)主要完成四部分工作:初始化系统
    员变量;以全局静态数组 g_pSystemDomainMemory
    对象,并将指针保存到 m_pSystemDomain 静态变量
    构造等功能使用;构造缺省的 AppDomain;构造

      以下内容为程序代码:                                                          

      SystemDomain*   
    SystemDomain::m_pSystemDomai
    n = NULL;
      static BYTE     g_pSystemDom
    ainMemory[sizeof(SystemDomain)];

      HRESULT SystemDomain::Attach()                  
      {                                                                            
      // 判断 SystemDomain 是否已经构造                            
      _ASSERTE(m_pSystemDomain == NULL);          
      if(m_pSystemDomain != NULL)                        
      return COR_E_EXECUTIONENGINE;                    

      // 跏蓟低?stub 管理器和 SystemDomain 的静态成员变量
      // ...                                                                  

      // 构造 SystemDomain 对象                                    
      m_pSystemDomain = ne
    w (&g_pSystemDomainMemory) S
    ystemDomain();
      if(m_pSystemDomain =
    = NULL) return COR_E_OUTOFME
    MORY;

      // 初始化 SystemDomain 对象                                  
      HRESULT hr = m_pSystemDomain->BaseDomain::Init(); // Setup the memory heaps

      if(FAILED(hr)) return hr;                            

      m_pSystemDomain->GetInterfaceVTableMapMgr().SetShared();


      // 构造缺省的 AppDomain                                          
      hr = m_pSystemDomain->CreateDefaultDomain();

      if(FAILED(hr)) return hr;                            

      // 构造 SharedDomain                                          
      hr = SharedDomain::Attach();                      

      return hr;                                                          
      }                                                                            

      值得注意的是,为了让 SystemDomai
    BaseDomain 的构造函数都为空,而初始
    码都使用类似的模式将构造和初始化分离
    SystemDomain::Attach 中直接被调用以
    SystemDomain::Init 函数则在上面提到

    n 的构造不会失败,SystemDomain 及其基类
    化代码放到 Init 方法中完成,CLR 中很多类型的代
    以保障构造成功。BaseDomain::Init 函数在
    初始化 SystemDomain 的父类;
    的 EEStartup 函数末尾才被调用,待会再详细讨论

      BaseDomain::Init 函
    一大堆成员变量外,主要负
    中存在的,这也是为什么我
    BaseDomain 之后,会将 Sy
    SystemDomain 负责载入的
    数(vmappdomain.cpp:310)除了
    担堆和缓存的初始化。CLR 中的
    们刚刚可以使用 EEHeap 命令列
    stemDomain 的接口 VTable 映
    mscorlib 中类型实际上是所以
    要负责初始化 BaseDomain 对象的
    堆,实际上是在每个 AppDomain
    举 AppDomain 的原因。在初始化
    射表设置为共享,这是因为
    AppDomain 中夹枰褂玫降摹?br>
      接着 SystemDomain::Attach 会调用
    (vmappdomain.cpp:2522)构造缺省的 App
    入用户指定 Assembly 执行。此函数只是
    Managed 方式构造新的 AppDomain 实例
    SystemDomain::CreateDefaultDomain 函数
    Domain,也就是前面试验中的 "Domain 0",用作载
    简单地调用 SystemDomain::NewDomain 函数以非
    ;然后将此 AppDomain 设置为缺省的 AppDomain。
      以下内容为程序代码:                                                          

      HRESULT SystemDomain::CreateDefa
    ultDomain()
      {                                                                            
      HRESULT hr = S_OK;                                          

      // 防止多次初始化                                                          
      if (m_pDefaultDomain != NULL)                    
      return S_OK;                                                      

      // 以非 Managed 方式构造新的 AppDomain 实例            
      AppDomain* pDomain = NULL;                          
      if (FAILED(hr = NewDomain(&pDomain)))    
      return hr;                                                          

      // 将此 AppDomain 设置为缺省的 AppDomain              
      pDomain->GetSecurityDescriptor()->SetDefaultAppDomainProperty();


      m_pDefaultDomain = pDomain;                        

      // ...                                                                  
      }                                                                            

      SystemDomain::NewDom
    例后,通知此 AppDomain
    tupSharedStatics 函数(vm
    ystem.SharedStatics。这
    .Runtime.Remoting.Identi
    ain 函数(vmappdomain.cpp:248
    其载入的 Assembly;最后会调
    appdomain.cpp:4583) 构造并初
    个类被用于生成全局唯一的 GUI
    ty.ProcessIDGuid 以及安全相
    0)比较简单,构造 AppDomain 实
    用 AppDomain::Se
    始化一个内部类 S
    D,在诸如 System
    关类型中被用到。

      在 SystemDomain::Att
    化 SharedDomain。此 Shar
    前写的一篇文章《.Net平台
    略,有兴趣的朋友可以仔细
    ach 函数的末尾,会调用 Share
    edDomain 负责载入 Appdomain-
    下CLR程序载入原理分析》刑?br>看看,这儿摘抄一段:
    dDomain::Attach 函数构造并初始
    neutral 的共享 Assembly。我以
    论了载入 Assembly 进行共享的策


      以下为引用:                                                                  

      以下三个参数用于指定配件载入优化策略.我们等会详细讨论.                      
      STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN   = 0x1 << 1,

      STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN   = 0x2 << 1,

      STARTUP_LOADER_OPTIMIZATION_MULTI_DOMAIN_HOST = 0x3 << 1,


      ...                                                                        

      CLR在执行一个配件时,会新建一个应
    使用到一个配件,就要涉及到前面提到的
    STARTUP_LOADER_OPTIMIZATION_SINGLE_D
    这样速度最快,管理最方便,但占用内存较
    STARTUP_LOADER_OPTIMIZATION_MULTI_DO
    等数据时,因为要保证每个应用域有独立
    用(使用STARTUP_LOADER_OPTIMIZATION_M
    Name的配件才会被多个应用域共享.
    用域,将此配件放入新的应用域.如果多个应用域同时
    配件载入优化策略了.最简单的方法是使用
    OMAIN标志,每个应用域拥有一份独立的配件的镜像,
    多.相对的是所有应用域共享一份配件的镜像,(使用
    MAIN标志)这样节约内存,但在此配件中存在静态变量
    的数据,所以会一定程度上影响效率.折中的方案是使
    ULTI_DOMAIN_HOST标志)此时,只有那些有Strong


      SharedDomain::Attach 函数(vmappd
    SystemDomain::Attach 类似,其也是在
    SharedDomain 对象,并饔?SharedDom
    函数(vmappdomain.cpp:6475)则首先调用
    Assembly 映射表。
    omain.cpp:6440)的实现比较简单,与
    g_pSharedDomainMemory 分配的全局静态内存区构造
    ain::Init 函数初始化之。而 SharedDomain::Init
    基类的初始化函数 BaseDomain::Init,然后初始化


      在完成 SystemDomain:
    SystemDomain::Init 函数
    :Attach 函数调用和异常等初始
    完成 SystemDomain 的初始化工
    化工作后,EEStartup 函数会调用
    作。
      SystemDomain::Init
    数;然后获取 Windows 系
    库所在 Assembly (mscorli
    函数(vmappdomain.cpp:1074)首
    统目录等配置信息;接着分别完
    b.dll);构造预分配异常对象;
    先初始化 fusion 系统关闭回调?br>成最重要的三项工作:载入 BCL
    构造并初始化全局字符串常量表。
      SystemDomain::LoadBa
    SystemDomain::LoadSystem
    der::StartupMscorlib 函
    初始化工作;最后从 mscor
    g_pArrayClass等等。
    seSystemClasses 函数(vmappdo
    Assembly 函数载入 mscorlib.d
    数间接调用 g_Mscorlib.Init (
    lib 中载入常用的一些类型,如

    main.cpp:1263)首先调用
    ll;然后通过 Bin
    Binder::Init) 完成 mscorlib 的
    g_pValueTypeClass、

      SystemDomain::Create
    刚获取的类型定义,构造预
    ackOverflowException 和
    堆栈和堆可能已经被破坏或
    Framework 2.0 中对此类问
    念,确保局部构造的确定性
    [2] Whidbey 中的改进》
    PreallocatedExceptions 函数(
    分配的三个异常对象:OutOfMem
    ExecutionEngineException。因
    溢出,不能再通过传统的内存分
    题更是进一步提出了 CER(Const
    等等。有兴趣的朋友可以参考我

    vmappdomain.cpp:1019)则使用刚
    oryException、St
    为这三种异常被引发的时候,CLR
    配方式进行构造。而 .NET
    rained Execution Regions)等概
    另外一篇文章《Finalization

      对全局字符串常量表的
    以字符串为值的全局 HashM
    率等等。有兴趣的朋友可以
    初始化就比较简单,实际上是初
    ap。用于优化字符串性能,保障
    参考我另外一篇文章《CLR中字
    始化了一个以字符串Hash值为键,
    跨 AppDomain 字符串传递的高效
    符串不变性的优化》。

      至此,CLR 在运行用户程序之前,启
    Domain 的流程基本上已经介绍完毕,下
    能够在空间和效率上达到最优。
    动 System Domain、Shared Domain 和 Default
    一节将介绍这三者如何搭配使用,使 CLR 在运行时



    上一页 下一页




    map