我们都知道,进程是操作系统进行资源调度和分配的基本单位,每个进程实际代表了当前应用程序从启动到结束的全部过程。对于Windows中的每一个.exe文件,在运行时都要由一个进程来承载它。与非托管.exe文件不同的是,托管.exe文件并没有直接将程序集加载到当前进程当中,而是将程序集加载到应用程序域中,然后将应用程序域加载到进程中。每个进程可以承载多个应用程序域。因此,对于托管程序,每个进程可以承载多个应用程序,这同时也提升了应用程序的性能,因为进程的切换所耗费的性能要多于应用程序域的切换。
1.1 应用程序域的特点
托管程序为什么要使用应用程序域呢?概括其优点如下:
1. 在一个应用程序中出现的错误不会影响其他应用程序。
2. 因为类型安全的代码不会导致内存错误,所以使用应用程序域可以确保在一个域中运行的代码不会影响进程中的其他应用程序。
3. 能够在不停止整个进程的情况下停止单个应用程序。
4. 使用应用程序域时,可以卸载在单个应用程序中运行的代码。
5. 在一个应用程序中运行的代码不能直接访问其他应用程序中的代码或资源。
为了强制实施此隔离,公共语言运行库禁止在不同应用程序域中的对象之间进行直接调用。要在各域之间传递对象,可以通过复制这些对象,或通过代理访问这些对象。如果复制对象,那么对该对象的调用为本地调用。也就是说,调用方和被引用的对象位于同一应用程序域中。如果通过代理访问对象,那么对该对象的调用为远程调用。在此情况下,调用方和被引用的对象位于不同的应用程序域中。域间调用所采用的远程调用基础结构与两个进程间的调用或两台计算机间的调用的基础结构相同。因此,被引用的对象的元数据必须对于两个应用程序域均可用,以便用 JIT 正确编译该方法调用。如果调用域对被调用对象的元数据没有访问权,则编译可能失败,并引发类型为 System.IO.FileNotFound 的异常。
1. 代码行为的作用范围由它运行所在的应用程序决定。
换言之,应用程序域将提供应用程序版本策略等配置设置、它所访问的任意远程程序集的位置,以及加载到该域中的程序集的位置信息。
2. 向代码授予的权限可以由代码运行所在的应用程序域来控制。
1.2 创建应用程序域
.NET提供了相关的类和方法来获取当前应用程序域的基本信息,也提供了创建和配置应用程序域的成员。其中,AppDomain 类是应用程序域的编程接口,此类包括各种方法,这些方法可以创建和卸载域、创建域中各类型的实例以及注册各种通知(如应用程序域卸载)。对于应用程序域的卸载,将在第3.1.3节做介绍。
代码清单1-1演示了如何创建一个应用程序域。
代码清单1-1 创建应用程序域
class Program
{
static void Main(string[] args)
{
AppDomain myDomain= AppDomain.CreateDomain("xuanhunDomain");//创建名为xuanhunDomain的应用程序域
Console.WriteLine("myDomain name is :{0}",myDomain.FriendlyName);//输出创建的程序域名称
Console.WriteLine("当前程序域的名称是:{0}", AppDomain.CurrentDomain.FriendlyName);//输出当前程序域名称
Console.ReadKey();
}
}
输出结果如图1-1所示。
图1-1 输出应用程序域名称
1.3 卸载应用程序域
当完成使用应用程序域时,可使用System.AppDomain.Unload方法将其卸载。Unload 方法会正常关闭指定的应用程序域。卸载过程中,没有新线程可以访问该应用程序域,并且会释放该应用程序域特定的所有数据结构。加载到应用程序域中的所有程序集都会被移除,无法再使用。如果应用程序域中的程序集不是特定于域的,则程序集的数据会保留在内存中,直到整个进程关闭。除了关闭整个进程,没有机制可以卸载非特定于域的程序集。在某些情况下,卸载应用程序域的请求不起作用,并导致CannotUnloadAppDomainException。代码清单1-2在代码清单1-1的基础上添加了卸载xuanhunDomain程序域的语句,并且在卸载后再次尝试输出该应用程序域的名称,这将引发CannotUnloadAppDomainException。
代码清单1-2 卸载应用程序域
class Program
{
static void Main(string[] args)
{
AppDomain myDomain= AppDomain.CreateDomain("xuanhunDomain");
Console.WriteLine("myDomain name is :{0}",myDomain.FriendlyName);
Console.WriteLine("当前程序域的名称是:{0}", AppDomain.CurrentDomain.FriendlyName);
AppDomain.Unload(myDomain);
try//试图访问被卸载的应用程序域
{
Console.WriteLine("myDomain name is :{0}", myDomain.FriendlyName);
}
catch (CannotUnloadAppDomainException e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
输出结果如图1-2所示。
图1-2 卸载应用程序域
将程序集加载到应用程序域的方法已经在第2章中做了介绍,这里就不再重复。下面接着讨论如何获得当前线程的应用程序域的相关信息,如代码清单1-3所示。
代码清单1-3 获得当前线程应用程序域相关信息
public void GetDomainInfo()
{
ActivationContext tContext = AppDomain.CurrentDomain.ActivationContext;//获取上下文信息
ApplicationIdentity tIdentity=AppDomain.CurrentDomain.ApplicationIdentity;//获取应用程序标识
System.Security.Policy.ApplicationTrust tTrust = AppDomain.CurrentDomain.ApplicationTrust;//获取当前程序的信任级别
string tDirectory = AppDomain.CurrentDomain.BaseDirectory;// 获取基目录,它由程序集冲突解决程序用来探测程序集
AppDomainManager tDomainManager = AppDomain.CurrentDomain.DomainManager;//获得初始化应用程序域时主机提供的域管理器
string tDynamicDirectory = AppDomain.CurrentDomain.DynamicDirectory;//获取目录,它由程序集冲突解决程序用来探测动态创建的程序集
System.Security.Policy.Evidence tEvidence = AppDomain.CurrentDomain.Evidence;//获取与此应用程序域相关联的 Evidence,它用做安全策略的输入
string tFrindlyName = AppDomain.CurrentDomain.FriendlyName;//获取此应用程序域的友好名称
int tId = AppDomain.CurrentDomain.Id;//获得一个整数,该整数唯一标识进程中的应用程序域
bool tIsFullTrusted = AppDomain.CurrentDomain.IsFullyTrusted;//表示当前应用程序域中的程序集是否为完全信任级别
bool tIsHomogenous = AppDomain.CurrentDomain.IsHomogenous;//获取一个值表示是否拥有对加载到当前应用程序域的所有程序集的权限集合
long tMSMemorySize = AppDomain.CurrentDomain.MonitoringSurvivedMemorySize;
long tMTAMemorySieze = AppDomain.CurrentDomain.MonitoringTotalAllocatedMemorySize;
TimeSpan tMTPTime = AppDomain.CurrentDomain.MonitoringTotalProcessorTime;
System.Security.PermissionSet tPermissionSet = AppDomain.CurrentDomain.PermissionSet;//获取权限集
string tRPath = AppDomain.CurrentDomain.RelativeSearchPath;//获取相对于基目录的路径,在此程序集冲突解决程序应探测专用程序集
AppDomainSetup tASetup = AppDomain.CurrentDomain.SetupInformation;//安装信息
bool tSCopyFiles = AppDomain.CurrentDomain.ShadowCopyFiles;//获取一个指示值,它表明加载到应用程序域中的所有程序集是否为影像复制的
}
在代码清单1-3中,使用AppDomain.CurrentDomain的属性来获取当前应用程序域的实例,可以从中获取当前应用程序域的相关信息,其中.NET 4.0中新增的属性如下:
q AppDomain.CurrentDomain.MonitoringSurvivedMemorySize。获取上次完全阻止回收后保留下来的、已知由当前应用程序域引用的字节数。
q AppDomain.CurrentDomain.MonitoringTotalAllocatedMemorySize。获取自从创建应用程序域后由应用程序域进行的所有内存分配的总大小(以字节为单位,不扣除已回收的内存)。
q AppDomain.CurrentDomain.MonitoringTotalProcessorTime。获取自从进程启动后所有线程在当前应用程序域中执行时所使用的总处理器时间。
q AppDomain.CurrentDomain.PermissionSet。获取沙箱应用程序域的权限集。
----------------注,本文部分内容改编自《.NET 安全揭秘》
阅读(2115) | 评论(0) | 转发(0) |