同一臺計算上的應用程序是通過進程來隔離的,每個應用程序都是加載到不同的進程中,從而達到應用程序的互不影響。操作系統【OS】通過進程控制塊【PCB】感知進程的存在,分析【PCB】的數據結構可以發現,【PCB】維護進程運行的內存空間,就是我們所說的虛擬內存,然后OS負責映射到實際的內存空間。應用程序互不影響簡單解釋就是說A應用程序不能隨意的操作B應用程序的內存空間,內存空間又與進程相互對應。因此可以理解進程就是應用程序運行的邊界。創建一個應用程序就要相應的創建一個進程,而創建進程需要耗費大量的資源并且進程與進程之間相互通信也比較困難,例如命名管道。
本文主角應用程序域【AppDomain】是.Net應用程序運行的邊界,多個應用程序域能夠創建在同一個進程里。這是為什么呢?所有的.Net應用程序都是運行在公共語言運行時【CLR】上,在公共語言運行時上運行的代碼稱作托管代碼。托管代碼有一項很重要的機制就是類型安全檢測,安全檢測包括這些代碼是否會嘗試訪問無效的內存地址?是否會嘗試執行某些導致進程(該代碼運行時所在的進程)無法正常進行的其他操作?,只有通過檢測的代碼才能稱之為類型安全,換句話說,.Net應用程序不要OS來管理應用程序運行的安全性,.Net應用程序通過CLR就能保證其運行安全。CLR就是通過應用程序域來隔離應用程序,創建應用程序域的資源耗費少,應用程序域之間通信也比較容易,通過代理來實現之間的通訊。
本文主要從三個方面簡單的介紹應用程序域:
1.DotNet應用程序如何加載運行
1.創建,卸載應用程序域
2.應用程序域之間的通信
轉眼畢業已經一年時間了,每天在與.Net Framework打交道,卻從沒有花時間去研究.Net Framework是怎么運行起來的。
.NET Framework 應用程序無論是像托管 .exe 程序集那樣自動調用的,還是使用非托管宿主 API 加載的,都需要一段稱為運行時宿主的代碼。運行時宿主會將運行庫加載到進程中,在進程中創建應用程序域,然后在這些應用程序域內加載和執行用戶代碼,常見的運行時宿主包括ASP.NET【啟動bs應用程序】,外殼程序可執行文件【啟動cs應用程序】,.Net Framework提供了創建管理宿主的com接口,可以自定義化自己的宿主對象。
將這個過程寫的詳細點:
1.執行任何托管代碼之前,宿主必須首先加載并初始化公共語言運行庫。由于此時運行庫尚未在進程中運行,.NET Framework 提供了一組被稱為宿主 API 的非托管 API,宿主可以利用它們來啟動運行庫。加載運行時的時候可以配置并行垃圾回收機制,是否加載程序優化,版本等信息。
2.加載并初始化公共語言運行庫后,宿主必須執行從非托管代碼到托管代碼的轉換,以執行托管宿主代碼和用戶代碼。在 .NET Framework 的早期版本中,托管宿主代碼通常在默認應用程序域中運行,但是 .NET Framework 2.0 版本提供了一個基類(即 AppDomainManager),用于實現自動加載到在進程中創建的每個應用程序域中的托管宿主代碼。
3.當宿主代碼完成從非托管代碼到托管代碼的轉換后,它必須新建一個或多個用于運行用戶代碼的應用程序域。
4.應用程序域加載和執行用戶代碼,可用來加載和運行托管代碼的方法都基于程序集。例如,System.AppDomain 和 System.Reflection.Assembly 類包含使宿主能夠加載程序集的方法。Load 方法具有各種形式:有些方法采用程序集名稱,有些方法采用程序集清單所在文件的完整文件系統路徑。這些方法用于加載先前已創建并保存到磁盤上的程序集。
公共語言運行庫已經過專門設計,支持各種類型的應用程序,包括從 Web 服務器應用程序到具有傳統的豐富 Windows 用戶界面的應用程序在內的所有應用程序。每種應用程序都需要一個運行庫宿主來啟動它。運行庫宿主將該運行庫加載到進程中,在該進程內創建應用程序域,并且將用戶代碼加載到該應用程序域中。因此加載運行時部分不需要用戶關系,只有有特殊需求是才會去自定義運行時的加載。用戶關系的核心問題就是如何創建應用程序域來加載用戶自定義的業務邏輯代碼。
創建和卸載應用程序域很簡單
//獲取當前線程正在其中運行的應用程序域名
string callingDomainName = Thread.GetDomain().FriendlyName;
Console.WriteLine(callingDomainName);
//獲取當前應用程序運行的一個程序集(AppDomain.ExcuteAssembley(string path))
string exeAssembly = Assembly.GetEntryAssembly().FullName;
Console.WriteLine(exeAssembly);
//通過AppDomain類的CreateDomain靜態方法創建一個域,
//CreateDomain有很多重載的方法,總有一個適合您
AppDomain adSecond = AppDomain.CreateDomain("Ad #2");
//卸載域
AppDomain.Unload(adSecond);
代碼背景假如用戶第一次請求站點的時候,AspNet宿主會創建一個新的應用程序域來處理這個web應用的所有請求,只是舉例,實際處理過程復雜的多。
class Program
{
static void Main(string[] args)
{
//獲取當前線程正在其中運行的應用程序域名
string callingDomainName = Thread.GetDomain().FriendlyName;
Console.WriteLine(callingDomainName);
//獲取當前應用程序運行的一個程序集(AppDomain.ExcuteAssembley(string path))
string exeAssembly = Assembly.GetEntryAssembly().FullName;
Console.WriteLine(exeAssembly);
// 配置應用程序域信息
AppDomainSetup ads = new AppDomainSetup();
ads.ApplicationName = "webSite #1";
// 創建應用程序域(模擬第一次web請求IIS)
AppDomain webSite = AppDomain.CreateDomain("webSite #1", null, ads);
string context = "http://www.cnblogs.com/prince_hui/";
//在創建域中創建一個運行時對象
HttpRuntime runtime =
(HttpRuntime)webSite.CreateInstanceAndUnwrap(
exeAssembly,
typeof(HttpRuntime).FullName
);
//runtime是新應用程序域中對象的代理,通過調用代理處理請求
runtime.ProcessRequest(context);
//卸載掉應用程序域,此時域中的對象也被回收
AppDomain.Unload(webSite);
try
{
runtime.ProcessRequest(context);
Console.WriteLine("Sucessful ProcessRequest.");
}
catch (AppDomainUnloadedException)
{
Console.WriteLine("Failed ProcessRequest; this is expected.");
}
Console.Read();
}
}
// 因為HttpRuntime繼承自MarshalByRefObject,
// 所以HttpRuntime的代理對象能夠在多個域中傳遞
public class HttpRuntime : MarshalByRefObject
{
//通過代理調用這個方法
public void ProcessRequest(string context)
{
// 獲取應用程序域的配置信息
AppDomainSetup ads = AppDomain.CurrentDomain.SetupInformation;
Console.WriteLine("WebSiteName: {0}",
ads.ApplicationName
);
//處理請求啦
Console.WriteLine("ProcessRequest '{0}' in new domain:'{1}'.",
context,
Thread.GetDomain().FriendlyName
);
}
}
運行結果如下:
作者:劉慧
出處:http://www.cnblogs.com/prince_hui/
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。