關於寄宿和AppDomain 微軟開發CLR時,將它實現成包含在一個DLL中的COM伺服器。 任何Windows應用程式都能寄宿(容納)CLR。(簡單來講,就是CLR在一個DLL中,通過引用這個DLL,可以實現包含CLR) CLR COM伺服器初始化會創建一個預設AppDomain,這個AppDom ...
關於寄宿和AppDomain
微軟開發CLR時,將它實現成包含在一個DLL中的COM伺服器。
任何Windows應用程式都能寄宿(容納)CLR。(簡單來講,就是CLR在一個DLL中,通過引用這個DLL,可以實現包含CLR)
CLR COM伺服器初始化會創建一個預設AppDomain,這個AppDomain只有在進程終結時才會被銷毀。
然而宿主程式還可以要求CLR創建額外的AppDomain。
因為創建進程開銷很大,並且需要大量記憶體來虛擬化進程的地址空間。
所以人們就像可不可以在一個進程上運行多個程式呢。
於是就在進程上寄宿CLR,並除了AppDomain的概念。AppDomain是為了提供隔離而設計的。
當然原因不止如此,寄宿為應用程式提供了自定義和可擴展性的能力,然而這會導致一些惡意dll去破壞應用程式的數據結構和代碼,還能利用安全上下文來訪問本來無權訪問的資源。
CLR的AppDomain可以解決這個問題,允許第三方的不受信任的代碼在現有進程中運行,而CLR保證數據結構、代碼和安全上下文不被濫用和破壞
AppDomain,看名字就知道:應用程式域。
以下為AppDomain的具體功能:
- 一個AppDomain的代碼不能訪問另一個AppDomain的代碼創建的對象。這樣就將兩個AppDomain隔離開來,這種隔離使AppDomain很容易在進程中卸載,不會影響其它AppDomain正在運行的代碼。
- AppDomain可以卸載。(前面提到過,預設的第一個AppDomain是不能卸載的)
- AppDomian可以單獨保護。(每個AppDomain都會有自己的許可權集)
- AppDomain可以單獨配置。(每個AppDomain都會有自己的配置設置,主要影響CLR在AppDomain中的載入方式。涉及搜索路徑、版本綁定重定向、捲影複製以及載入器優化)
一個進程上面運行一個CLR COM伺服器,然後該CLR管理多個AppDomain,每個AppDomain可以有自己獨立的程式。有的程式集是可以在AppDomain中共用的,比如MSCorLib.dll這種與.NET密不可分的類型,在CLR初始化時會被自動載入,有自己獨有的loader堆去維護類型對象,且作為"AppDomain"中立的方式為所有AppDomain共用。
跨越AppDomain邊界訪問對象
前面講到AppDomain就是為了隔離而設計的,且一個AppDomain的代碼不能訪問另一個AppDomain的代碼。
然而要去做到也不是沒有辦法,為了實現兩個AppDomain之間的通信。
按照AppDomain通信可以將類型分為三種:按引用封送類型,按值封送類型,完全不能封送的類型。
進程中可以有多個線程和多個AppDomain,然而它們不是一一對應的。
雖然一個線程一次只能執行一個AppDomain中的代碼,但是一個線程可以執行一個AppDomain的代碼後再去執行另一個AppDomain的代碼,並且還可以查看自己在哪個AppDomain中。
然而這裡由於感覺用不到關係,而且有點小複雜,所以就只領會了中心思想:
按引用封送:實際上就是AppDomain比如B傳給另一個AppDomain比如A一個引用對象b,當執行b的函數時,實際上是線程又切回了B,調用完後再切回A。
按值封送:這個實際上就是將一個AppDomain中的對象序列化然後放到另一個AppDomain進行反序列化中。
卸載AppDomain
調用靜態方法AppDomain.Unload即可。
卸載AppDomain會卸載其中的所有程式集,釋放loader堆。
主要步驟:
- CLR掛起進程中執行過托管代碼的所有線程
- 查看那些線程正在執行要卸載的AppDomain中的代碼,有就強迫對應的線程拋出一個ThreadAbortException(線程中止異常)並恢復執行線程,這將導致執行遇到的所有的finally塊以清理資源。如果沒有代碼捕捉到這個異常,那麼finally就不會執行,但是線程會終止,但進程可繼續運行。(其它所有未處理的異常我們在前面講過會終止進程,而這裡只會終止這個線程.
- 執行完第二步,所有的線程都會離開此AppDomain,CLR進入前面垃圾回收類似的標記階段,標記垃圾。
- CLR強制執行垃圾回收,這些對象的Finalize方法被調用來正確清理占用的資源。
- CLR恢復所有線程的執行。
任何時候都只會有一個線程來調用Unload方法,不會有多個線程同時調用。
宿主如何使用AppDomain
控制台UI應用程式,NT Service應用程式,Windows窗體應用程式和WPF應用程式都是自寄宿(即自己容納CLR)的應用程式。
它們都有托管exe文件,托管exe文件初始化進程後,會載入“墊片”(即MSCorEE.dll),墊片檢查應用程式集的CLR頭信息,決定載入哪個版本的CLR到進程中,CLR載入完後再次檢查CLR頭去茶砸後Main函數,然後調用該方法後程式才算真正啟動起來。
再舉一個例子,就拿ASP.NET來說,客戶端請求一個Web應用程式時,如果是第一次請求,那麼就ASP.NET要求CLR創建新的AppDomain,每個Web應用程式根據虛擬根目錄來標識,然後讓CLR將包含應用程式所公開類型的程式集載入到新的AppDomain中,創建實例,並調用其中方法。如果不是第一次請求就不會創建新的AppDomain。然而如果客戶端請求不同的Web應用程式,也會創建新的AppDomain。貌似就說的是IIS里的那些不同的網站。
小總結:
到這一步,還是打算講一下自己對這個東西的理解。
對於.NET的世界而言,都是在一個進程之上載入CLR(CLR COM伺服器),然後在這個CLR上再載入不同的AppDomain。
對於我們平常寫的那些簡單用.net創建的程式如控制台程式,實際上也是在一個托管exe上再載入CLR,而此時會載入了預設的一個AppDomain。此exe可以作為一個宿主去載入更多的AppDomain,然而這就需要自己手動去載入了。
而對於那些不是.NET的非托管應用程式,也是可以載入CLR,而此時也會載入了預設的一個AppDomain,實際上它也可以作為一個宿主去載入更多的AppDomain。
可能有偏差,還望指正!
PS:
本章內容只擇其精要寫了一下,具體的AppDomain的玩法需要的時候再進一步深究。(話說回來,總有一種一輩子玩不到這個東西的感覺)