應用程序域基礎
我們都知道,進程是操作系統進行資源調度和分配的基本單位,每個進程實際代表了當前應用程序從啟動到結束的全部過程。對於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 (CannotUnloadAppDomainExceptione)
{
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 安全揭秘》