天天看点

Threads, Process, and 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

AppDomain

s. 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 process,

AppDomain

s still offer many of the features that a Windows process offers: separate memory spaces and access to resources.

AppDomain

s 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

AppDomain

s. 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:

Threads, Process, and AppDomains

Collapse | Copy Code

using System;
class Test {
static void Main() {
AppDomain d = AppDomain.CreateDomain("NewDomain");
Console.WriteLine("Host domain:  "  + AppDomain.CurrentDomain.FriendlyName);
Console.WriteLine("Child domain:  "   + d.FriendlyName);
  }
}
      

Output:

Threads, Process, and AppDomains
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:

Threads, Process, and AppDomains
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;

public class Test {
public static void Main() {
 AssemblyName mscorlib = new AssemblyName();
            mscorlib.Name = "mscorlib";
            mscorlib.Version = new Version(2, 0, 0, 0);
            mscorlib.CultureInfo = CultureInfo.InvariantCulture;
            mscorlib.SetPublicKeyToken(new byte[] {
                0xb7, 0x7a, 0x5c, 056, 0x19, 0x34, 0xe0, 0x89 });
            mscorlib.ProcessorArchitecture = ProcessorArchitecture.X86;

            Console.WriteLine(mscorlib);
        }
}      

When we compile this code, we get:

Threads, Process, and AppDomains
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

:

Threads, Process, and AppDomains
public static void Main()
        {
            // Set up a new AppDomain, and load the DLL inside of it:
            AppDomain ad = AppDomain.CreateDomain("Isolated");

            // …

            // We can then dump the contents of the AppDomain:
            AppDomain.Unload(ad);
        }
}      

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

Threads, Process, and AppDomains
using System;
using System.Collections.Generic;
public class Test {
public static void Main()
        {
            Console.WriteLine("Before: {0}", Environment.WorkingSet);

            // Loads System.dll (if it's not already loaded):
            Type uriType = typeof(Uri); 
            Console.WriteLine("After loading System.dll: {0}", Environment.WorkingSet);

            // Loads System.dll into 10 new AppDomains:
            List ads = new List();
            for (int i = 0; i < 10; i++)
            {
                AppDomain ad = AppDomain.CreateDomain(i.ToString());
                ad.DoCallBack(delegate { Type t = typeof(Uri); });
                ads.Add(ad);
            }
            Console.WriteLine("After loading System.dll into 10 AppDomains:  					{0}", Environment.WorkingSet);
            // Unload the appdomains and check the working set:
            foreach (AppDomain ad in ads)
                AppDomain.Unload(ad);
            Console.WriteLine("After unloading the AppDomains: {0}", 
						Environment.WorkingSet);
        }
}      

The output:

Threads, Process, and AppDomains
Before: 5943296
After loading System.dll: 6107136
After loading System.dll into 10 AppDomains: 9572352
After unloading the AppDomains: 9732096      
Threads, Process, and AppDomains
using System;
using System.Threading;
public class Test {
public static void Main()
        {
            // Retrieve the info:
            int minWorkerThreads, maxWorkerThreads, availableWorkerThreads;
            int minIoThreads, maxIoThreads, availableIoThreads;
            ThreadPool.GetMinThreads(out minWorkerThreads, out minIoThreads);
            ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxIoThreads);
            ThreadPool.GetAvailableThreads(
                out availableWorkerThreads, out availableIoThreads);

            // Now print it out:
            Console.WriteLine("Min/max/available worker threads: {0}/{1}/{2}",
                minWorkerThreads, maxWorkerThreads, availableWorkerThreads);
            Console.WriteLine("Min/max/available IO threads: {0}/{1}/{2}",
                minIoThreads, maxIoThreads, availableIoThreads);
        }
}      
Threads, Process, and AppDomains
using System;
using System.Diagnostics;
using System.Threading;
public class Test {
 public static void Main()
        {
            Console.WriteLine("Current process:");
            PrintProcess(Process.GetCurrentProcess());

            Console.WriteLine("IE processes:");
            Process[] iexplorerProcs = Process.GetProcessesByName("iexplore");
            foreach (Process p in iexplorerProcs) PrintProcess(p);

            Console.WriteLine("All active processes:");
            Process[] allProcs = Process.GetProcesses();
            foreach (Process p in allProcs) PrintProcess(p);
        }
  private static void PrintProcess(Process p)
        {
            Console.WriteLine("  -> {0} - {1}", p.ProcessName, p.PeakWorkingSet64);
        }
 }      
Threads, Process, and AppDomains
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

下一篇: 感想

继续阅读