|
我們知道,進(jìn)程是操作系統(tǒng)用于隔離眾多正在運(yùn)行的應(yīng)用程序的機(jī)制。在.Net之前,每一個應(yīng)用程序被加載到單獨(dú)的進(jìn)程中,并為該進(jìn)程指定私有的虛擬內(nèi)存。進(jìn)程不能直接訪問物理內(nèi)存,操作系統(tǒng)通過其它的處理把這些虛擬內(nèi)存映射到物理內(nèi)存或IO設(shè)備的某個區(qū)域,而這些物理內(nèi)存之間不會有重疊,這就決定了一個進(jìn)程不可能訪問分配給另一個進(jìn)程的內(nèi)存。相應(yīng)地,運(yùn)行在該進(jìn)程中的應(yīng)用程序也不可能寫入另一個應(yīng)用程序的內(nèi)存,這確保了任何執(zhí)行出錯的代碼不會損害其地址空間以外的應(yīng)用程序。在這種機(jī)制下,進(jìn)程作為應(yīng)用程序之間一個獨(dú)立而安全的邊界在很大程度上提高了運(yùn)行安全。 進(jìn)程的缺點(diǎn)是降低了性能。許多一起工作的進(jìn)程需要相互通信,而進(jìn)程卻不能共享任何內(nèi)存,你不能通過任何有意義的方式使用從一個進(jìn)程傳遞到另一個進(jìn)程的內(nèi)存指針。此外,你不能在兩個進(jìn)程間進(jìn)行直接調(diào)用。你必須代之以使用代理,它提供一定程度的間接性。雖然,使用動態(tài)連接庫dll讓所有的組件運(yùn)行在同一空間,一定程度上可以提高性能,但這些組件相互影響,一個組件的錯誤將極有可能導(dǎo)致整個應(yīng)用程序的崩潰,“dll地獄”更是讓許多應(yīng)用程序難以避免。 在.Net中,應(yīng)用程序有了一個新的邊界:應(yīng)用程序域(以下簡稱域)。它是一個用于隔離應(yīng)用程序的虛擬邊界。為了禁止不應(yīng)交互的代碼進(jìn)行交互,這種隔離是必要的。.Net的應(yīng)用程序在域?qū)哟紊线M(jìn)行隔離,一個域中的應(yīng)用程序不能直接訪問另一個域中的代碼和數(shù)據(jù)。這種隔離使得在一個應(yīng)用程序范圍內(nèi)創(chuàng)建的所有對象都在一個域內(nèi)創(chuàng)建,確保在同一進(jìn)程中一個域內(nèi)運(yùn)行的代碼不會影響其他域內(nèi)的應(yīng)用程序,大大提高了運(yùn)行的安全。 .Net結(jié)構(gòu)中,由于公共語言運(yùn)行庫能夠驗(yàn)證代碼是否為類型安全的代碼,所以它可以提供與進(jìn)程邊界一樣大的隔離級別,其性能開銷也要低得多。你可以在單個進(jìn)程中運(yùn)行幾個域,而不會造成進(jìn)程間調(diào)用或切換等方面的額外開銷。這種方法是把任何一個進(jìn)程分解到多個域中,允許多個應(yīng)用程序在同一進(jìn)程中運(yùn)行,每個域大致對應(yīng)一個應(yīng)用程序,運(yùn)行的每個線程都在一個特殊的域中。如果不同的可執(zhí)行文件都運(yùn)行在同一個進(jìn)程空間中,它們就能輕松地共享數(shù)據(jù)或直接訪問彼此的數(shù)據(jù)。這種代碼同運(yùn)行同一個進(jìn)程但域不同的類型安全代碼一起運(yùn)行時是安全的。在一個進(jìn)程內(nèi)運(yùn)行多個應(yīng)用程序的能力顯著增強(qiáng)了服務(wù)器的可伸縮性。 域是.Net 帶來的一個重要改進(jìn),它不僅將眾多在運(yùn)行的應(yīng)用程序隔離開來,還不影響彼此間通信。雖然,公共語言運(yùn)行庫禁止在不同域中的對象之間進(jìn)行直接調(diào)用,但我們可以復(fù)制這些對象,或通過代理訪問這些對象。如果以前一種方式,那么對該對象的調(diào)用為本地調(diào)用。也就是說,調(diào)用方和被引用的對象位于同一域中。如果通過代理訪問對象,調(diào)用方和被引用的對象位于不同的域中,對該對象的調(diào)用被視為遠(yuǎn)程調(diào)用,這種情形與兩個進(jìn)程間的調(diào)用或兩臺計(jì)算機(jī)間的調(diào)用結(jié)構(gòu)大致相同。這時,需要被引用對象的元數(shù)據(jù)對于兩個域均可用,以便.Net即時編譯JIT能正確執(zhí)行。 在.Net中,線程是公共語言運(yùn)行庫用來執(zhí)行代碼的操作系統(tǒng)構(gòu)造。在運(yùn)行時,所有托管代碼均加載到一個域中,由特定的操作系統(tǒng)線程來運(yùn)行。然而,域和線程之間并不具有一一對應(yīng)關(guān)系。在任意給定時間,單個域中可以執(zhí)行不止一個線程,而且特定線程也并不局限在單個域內(nèi)。也就是說,線程可以跨越域邊界,不為每個域創(chuàng)建新線程。當(dāng)然,在指定時刻,每一線程都只能在一個域中執(zhí)行。運(yùn)行庫會跟蹤所有域中有哪些線程正在運(yùn)行。通過調(diào)用.Net類庫的 Thread.GetDomain 方法,你還可以確定正在執(zhí)行的線程所在的域。 作為公共語言運(yùn)行庫的隔離單元,域在進(jìn)程中創(chuàng)建和運(yùn)行。.Net結(jié)構(gòu)中,運(yùn)行時宿主(也叫作運(yùn)行時主機(jī))是負(fù)責(zé)將運(yùn)行時載入進(jìn)程并在域中執(zhí)行用戶代碼和托管代碼的應(yīng)用程序。運(yùn)行時宿主包括ASP.Net、瀏覽器Internet Explorer 和 Windows等外殼程序,負(fù)責(zé)創(chuàng)建進(jìn)程和默認(rèn)域,例如,Asp.Net為每個運(yùn)行在web服務(wù)器上的web應(yīng)用程序創(chuàng)建一個域。瀏覽器Internet explore創(chuàng)建運(yùn)行受管制控件的域。 對多數(shù)應(yīng)用程序,你并不必須創(chuàng)建相應(yīng)的域,每次CLR在初始化一個進(jìn)程時,將創(chuàng)建默認(rèn)域,并使該進(jìn)程運(yùn)行于這個默認(rèn)域下。然而,默認(rèn)域不能由任何系統(tǒng)調(diào)用來卸載,該域只有在進(jìn)程被卸載之后才能被銷毀。如果直接在默認(rèn)域下編程或運(yùn)行代碼,而由于某種原因域的代碼崩潰了,那么就有使得整個服務(wù)隨之崩潰的風(fēng)險。 于是,針對不同的應(yīng)用程序,應(yīng)該創(chuàng)建和配置相應(yīng)的域并載入適當(dāng)?shù)某绦蚣?。.Net為此提供了豐富的類庫。其中,AppDomain 類是域的編程接口,其大量的(重載)方法能完成以下任務(wù): · 創(chuàng)建域 · 在域中加載程序集和類型 · 枚舉域中的程序集和線程 · 卸載域 創(chuàng)建新域時,使用AppDomain 類的靜態(tài)方法CreateDomain。你可以為域命名并按該名稱來引用域。下面的示例語句創(chuàng)建新域,并為它指定名稱 MyDomain:
然后你可以查詢當(dāng)前域的名稱和新創(chuàng)建子域的名稱:
在這里,屬性FriendlyName表示的是域的友好名稱,友好名稱通過從程序集的基本代碼中去除目錄路徑而形成。例如,文件名為 "d:\MyAppDomain\MyAssembly.exe" 的程序集加載到默認(rèn)域中,域的友好名稱就是 "MyAssembly.exe"。 更一般的是,在創(chuàng)建域之前,先設(shè)置好域的參數(shù),這可以通過類AppDomainSetup來完成。該類的ApplicationBase 屬性定義應(yīng)用程序的根目錄, AppDomainSetup 類還有一個極重要的屬性變量LoaderOptimizzation,取值可以是MultiDomain,MultiDomainHost和SignleDomain等,用以指定被加載程序集的類別(共享程序集或域?qū)S贸绦蚣?,例如,以下語句把程序集設(shè)置為域?qū)S贸绦蚣?
對以上兩個方面簡單歸納一下,對域的典型操作就包括:設(shè)置參數(shù)然后創(chuàng)建兩個步驟,語句示例如下:
當(dāng)使用完域時,可使用AppDomain類Unload()靜態(tài)方法將其卸載。要卸載進(jìn)程中在運(yùn)行的托管代碼,只能卸載代碼運(yùn)行時所在的域而不能卸載單獨(dú)的程序集或類型,Unload方法會正常關(guān)閉指定的域。這時,載入域的所有程序集都會被移除,并且無法再使用。不過,如果域中的程序集對域是非特定的(域無關(guān)程序集,也即共享程序集),則程序集的數(shù)據(jù)還會保留在內(nèi)存中,直至整個進(jìn)程關(guān)閉。除了關(guān)閉整個進(jìn)程,沒有機(jī)制可以卸載這類程序集。由于一個進(jìn)程中允許包含多個域,某個域可以在不停止整個進(jìn)程的情況下卸載。以這樣的方式卸載不再需要的代碼,可以減少內(nèi)存占用并極大提高應(yīng)用程序的可縮放性。此外,由于線程并不與域一一對應(yīng),當(dāng)域中存在活動線程時,調(diào)用AppDomain.Unload方法可能無法將域卸載并導(dǎo)致異常。 從上面的論述不難看出:要運(yùn)行應(yīng)用程序,必須首先將程序集(.Net下經(jīng)編譯產(chǎn)生,包含IL中間語言、元數(shù)據(jù)及清單等)加載到域中。而且一個域中可裝載多個程序集。默認(rèn)情況下,公共語言運(yùn)行庫自動將一個程序集加載到包含引用該程序集的代碼的域。通過此方法,該程序集的代碼和數(shù)據(jù)獨(dú)立于使用該程序集的應(yīng)用程序。 自行創(chuàng)建域的好處之一便是可以指定如何裝載程序集。在域中有以下兩種方式加載程序集: 1、將當(dāng)前程序集加載入單獨(dú)的域中,同一個程序集可能有多個副本; 2、以非特定于域的形式加載程序集,讓一個程序集在多個域間共享; 這兩種方式各自偏重于安全性和性能,需要視具體情況在二者之間權(quán)衡。具體地,在 .Net 框架中,System.Reflection.Assembly 類提供以下靜態(tài)方法將程序集加載至域: · Load()在給頂程序集名稱的前提下,加載該程序集:
· LoadFrom()在已知程序集文件名或路徑等信息的情況下加載程序集:
參考資料: 《Microsoft .NET Framework程序設(shè)計(jì)》《.NET Framework高級編程》《.NET框架精髓 》等 |
|
|