Threads, Process, and AppDomains

本文深入探讨了.NET Framework的工作原理,包括其与Windows操作系统的关系、CLR的作用及如何通过AppDomains实现应用隔离。

Introduction: How It Works

The .NET Framework runs on top of the Windows operating system and as such, must be built using technologies that Windows can interface with. All managed modules and assembly files must use the PE (Portable Executable) format and must therefore be either a Windows executable or a Windows DLL. Further, the CLR and mscorlib.dll are the two most important components of the .NET Framework. The two of the most powerful resultant features of the .NET Framework are hosting and AppDomains. An Application Domain is a light-weight process that functions as a unit of isolation within that process to further function as a logical container that allows multiple assemblies to run within a single process which prevents them from directly accessing other assemblies’ memories. While considered a light-weight processAppDomains still offer many of the features that a Windows process offers: separate memory spaces and access to resources. AppDomains are actually more efficient than a process by enabling multiple assemblies to be run in separate application domains without the overhead of launching separate processes.

If you think about it, a Window process is normally written in C/C++. Most of the application DLLs and nearly all of the system DLLs are written in the native C language. But when developing the CLR Microsoft implemented it as a COM server contained inside a DLL: that is, Microsoft defined a standard COM interface for the CLR and assigned GUIDs (the unique identifiers that represent both the physical and the logical identifiers of a COM component) to this interface and the COM server. When you install the .NET Framework, the COM server representing the CLR is registered in the Windows registry just like any other COM component. The MSCorEE.h C++ header file ships with the .NET Framework SDK. This header file defines the GUIDs and the unmanaged ICLRRuntimeHost interface definition. Any Windows application can host the CLR but you don't instantiate the CLR COM server by calling CoCreateInstance; instead your unmanaged host should call the CorBindToRuntimeEx function, or another similar function, all of which are declared in the MSCorEE.h header file. The CorBindToRuntimeEx function is implemented in the MsCorEE.dll file that resides in the %systemroot%/system32 directory. This DLL then, is not a managed assembly like mscorlib.dll, but rather is called a shim. Its job is to determine which version of the CLR to create: either one for a server or one for a stand-alone desktop. The shim DLL does not contain the CLR COM server itself.

When the CorBindToRuntimeEx function is called, its arguments allow the host to specify which version of the CLR it would like to create, usually .NET 2.0 version 2.0.50727, as well as some other settings. Because the CorBindToRuntimeEx function gathers information about the number of CPUs installed on the machine, it can decide which version of the CLR to load -- the shim might not load the version that the host requested. This is why the CorBindToRuntimeEx function gathers this additional information. By default, when a managed executable starts, the shim examines the executable file and extracts the information indicating the version of the CLR that the application was built and tested with. The CorBindToRuntimeEx function returns a pointer to the unmanaged ICLRRuntimeHost interface. The hosting application can call methods defined by this interface to:

  • Set Host Managers: Tell the CLR that the host wants to be involved in making decisions related to memory allocations, thread scheduling/synchronization, assembly loading, etc.
  • Get Host Managers: Tell the CLR to prevent the use of some classes/members
  • Initialize and start the CLR
  • Load an assembly and execute code in it
  • Stop the CLR, thus preventing any managed code from running in the Windows process.

When the CLR COM server initializes, it creates an AppDomain, which then functions as that logical container for a set of assemblies. The first AppDomain is created when the CLR initialized is called the default AppDomain; this AppDomain is destroyed only when the Windows process terminates. But in addition to the default AppDomain, a host using either unmanaged COM interface methods or managed type methods can instruct the CLR to create additional AppDomains. Note that the entire purpose of the AppDomain is to provide isolation. Application domains are implemented in the .NET Framework using the System.AppDomain class. To use an application domain, you create an instance of the AppDomain class, and then execute an assembly within that domain:

 Collapse  |  Copy Code
 System;
 Test {
  Main() {
AppDomain d = AppDomain.CreateDomain();
Console.WriteLine(  + AppDomain.CurrentDomain.FriendlyName);
Console.WriteLine(   + d.FriendlyName);
  }
}

Output:

 Collapse  |  Copy Code
c:\Windows\MICROS~1.NET\FRAMEW~1\V20~1.507>ADomain.exe
Host domain:  ADomain.exe
Child domain:  NewDomain

Here is an example of constructing an assembly name:

 Collapse  |  Copy Code
 System;
 System.Collections;
 System.Collections.Generic;
 System.Diagnostics;
 System.Globalization;
 System.IO;
 System.Reflection;
 System.Security;
 System.Security.Permissions;
 System.Security.Policy;

  Test {
   Main() {
 AssemblyName mscorlib =  AssemblyName();
            mscorlib.Name = ;
            mscorlib.Version =  Version(, , , );
            mscorlib.CultureInfo = CultureInfo.InvariantCulture;
            mscorlib.SetPublicKeyToken( [] {
                0xb7, 0x7a, 0x5c, , 0x19, 0x34, 0xe0, 0x89 });
            mscorlib.ProcessorArchitecture = ProcessorArchitecture.X86;

            Console.WriteLine(mscorlib);
        }
}

When we compile this code, we get:

 Collapse  |  Copy Code
C:\Windows\MICROS~1.NET\FRAMEW~1\V20~1.507>AssemblyName.exe


mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c381934e089

And this is a basic look at unloading the assembly by creating an AppDomain:

 Collapse  |  Copy Code
   Main()
        {
            
            AppDomain ad = AppDomain.CreateDomain();

            

            
            AppDomain.Unload(ad);
        }
}

Now here is a test if System.dll is unloaded:

 Collapse  |  Copy Code
 System;
 System.Collections.Generic;
  Test {
   Main()
        {
            Console.WriteLine(, Environment.WorkingSet);

            
            Type uriType = (Uri); 
            Console.WriteLine(, Environment.WorkingSet);

            
            List ads =  List();
             ( i = ; i < ; i++)
            {
                AppDomain ad = AppDomain.CreateDomain(i.ToString());
                ad.DoCallBack( { Type t = (Uri); });
                ads.Add(ad);
            }
            Console.WriteLine(, Environment.WorkingSet);
            
             (AppDomain ad  ads)
                AppDomain.Unload(ad);
            Console.WriteLine(, 
						Environment.WorkingSet);
        }
}

The output:

 Collapse  |  Copy Code
Before: 5943296
After loading System.dll: 6107136
After loading System.dll into 10 AppDomains: 9572352
After unloading the AppDomains: 9732096

Processes in the .NET Framework correspond one to one with a process in Windows. A process’s primary purpose is to manage per-program resources; this includes a shared virtual address space among all threads running in that process, a HANDLE table, a shared set of DLLs (mapped into the same address space), and more other process-wide data that is stored in the Process Environment Block (PEB). Problems with one process will normally not affect other processes because of this isolation. But because of inter-process communication and system-wide shared resources – such as files, memory-mapped I/O, and named kernel objects – a process can interfere with another process. A single managed process (one which has loaded the CLR) can contain many AppDomains. It is suggested that the reader read up on and read some articles on the Thread Pool and the APM Programming Model. These topics involve a pool of threads in which there is one thread pool per process, and the ability to execute code asynchronously during a computation process. Below is some sample to exemplify getting information about a thread pool:

 Collapse  |  Copy Code
 System;
 System.Threading;
  Test {
   Main()
        {
            
             minWorkerThreads, maxWorkerThreads, availableWorkerThreads;
             minIoThreads, maxIoThreads, availableIoThreads;
            ThreadPool.GetMinThreads( minWorkerThreads,  minIoThreads);
            ThreadPool.GetMaxThreads( maxWorkerThreads,  maxIoThreads);
            ThreadPool.GetAvailableThreads(
                 availableWorkerThreads,  availableIoThreads);

            
            Console.WriteLine(,
                minWorkerThreads, maxWorkerThreads, availableWorkerThreads);
            Console.WriteLine(,
                minIoThreads, maxIoThreads, availableIoThreads);
        }
}

Compile and run this code to get information about the CLR's internal code that manages the thread pool. Now is a final example of getting all of the active processes listed while an instance of an unmanaged host, Internet Explorer, is running:

 Collapse  |  Copy Code
 System;
 System.Diagnostics;
 System.Threading;
  Test {
    Main()
        {
            Console.WriteLine();
            PrintProcess(Process.GetCurrentProcess());

            Console.WriteLine();
            Process[] iexplorerProcs = Process.GetProcessesByName();
             (Process p  iexplorerProcs) PrintProcess(p);

            Console.WriteLine();
            Process[] allProcs = Process.GetProcesses();
             (Process p  allProcs) PrintProcess(p);
        }
     PrintProcess(Process p)
        {
            Console.WriteLine(, p.ProcessName, p.PeakWorkingSet64);
        }
 }

Examine the output:

 Collapse  |  Copy Code
Current process:
  -> Procc - 6279168
IE processes:
  -> iexplore - 70152192
All active processes:
  -> GoogleToolbarUser - 6385664
  -> WINWORD - 54874112
  -> GoogleToolbarNotifier - 8638464
  -> svchost - 73224192
  -> winlogon - 6696960
  -> spoolsv - 10362880
  -> svchost - 12292096
  -> svchost - 2220032
  -> cmd - 2166784
  -> audiodg - 15163392
  -> hpqste08 - 16248832
  -> svchost - 9318400
  -> notepad - 5001216
  -> svchost - 6324224
  -> svchost - 3137536
  -> SearchIndexer - 23379968
  -> csrss - 13602816
  -> hpwuSchd2 - 2953216
  -> WZQKPICK - 4448256
  -> taskeng - 5509120
  -> Procc - 6856704
  -> hpqtra08 - 11644928
  -> wininit - 4194304
  -> svchost - 30564352
  -> svchost - 3657728
  -> unsecapp - 4689920
  -> svchost - 99524608
  -> svchost - 7344128
  -> notepad - 5156864
  -> svchost - 6287360
  -> cmd - 2871296
  -> taskeng - 9412608
  -> explorer - 71344128
  -> svchost - 11722752
  -> lsm - 3989504
  -> SLsvc - 14442496
  -> MSASCui - 9420800
  -> ieuser - 8790016
  -> dwm - 4603904
  -> lsass - 8232960
  -> svchost - 111050752
  -> notepad - 5521408
  -> svchost - 3964928
  -> FlashUtil10b - 4689920
  -> services - 7118848
  -> WmiPrvSE - 5750784
  -> iexplore - 70152192
  -> smss - 778240
  -> svchost - 33824768
  -> csrss - 5222400
  -> System - 10354688
  -> svchost - 5210112
  -> ntvdm - 4370432
  -> Idle - 0

References

  • The CLR via C#, 2nd Edition, by Jeffrey Richter

History
















本文转自cnn23711151CTO博客,原文链接:http://blog.51cto.com/cnn237111/508444 ,如需转载请自行联系原作者




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值