重溫CLR(一)CLR基礎

2022-07-07 10:18:10 字數 3760 閱讀 3934

如果乙個c#developer,對clr沒有了解,那就只能是入門級別。未來.net core是趨勢,但是.net core 也是基於coreclr的,而clr和coreclr其實差別不大,從runtime那部分看幾乎沒有區別,程序管理,gc,jit這些基本上是一樣的。

clr vie c#這本書很久之前就已經讀過了,但是一直沒有寫個像樣的總結。這次我重新讀了一遍,把我認為比較重要的點記錄下來。

clr(common language runtime,公共語言執行時)是乙個由多種編譯語言使用的「執行時」。clr的核心功能(比如記憶體管理、程式集載入、安全性、異常處理和執行緒同步)可由面向clr的所有語言使用。

事實上,在執行時,clr根本不關心開發人員用哪一種語言寫源**。無論選擇哪一種語言及其編譯器,結果都是託管模組(managed module)。

native code compilers生成的是面向特定cpu架構(比如x84,x64或arm)的**。相反,每個面向clr的編譯器生成的都是il(中間語言)**。il**有時稱為託管**(managed code),因為clr管理他的執行。

元資料:(metadata)元資料簡單地說就是乙個資料表集合。一些資料表描述了模組中定了什麼(比如型別及成員),另一些描述了模組引用了什麼(比如匯出的型別及其成員)。元資料是一些老技術的超集,不如com的型別庫和idl檔案,元資料總是與包含il**的檔案管理。元資料有多種用途,下面僅列舉一部分:

1 元資料避免了編譯時對原生c/c++頭和 庫檔案的需求,因為在實現型別/成員的il**檔案中,已包含有關引用型別/成員的全部資訊。編譯器直接從託管模組讀取元資料。

2 visual studio用元資料幫助你寫**。智慧型感知(intellisense)技術會解析元資料,告訴你乙個型別提供了哪些方法、屬性、事件和字段。以及相應引數。

3 clr的**驗證過程使用元資料確保**只執行」型別安全」的操作

4 元資料允許垃圾**器跟蹤物件生成期。垃圾**器能判斷任何物件的型別,並從元資料知道哪個物件中的那些字段引用了其他物件。

clr實際不和模組工作,他和程式集工作。程式集(assembly)是抽象概念,首先是乙個或多個模組/資源檔案的邏輯性分組。其次,程式集是重用、安全性以及版本控制的最小單元。在clr的世界中,程式集相當於」元件」。

下圖有助於你理解程式集。圖中一些託管模組和資源(或資料)檔案準備交由乙個工具(編譯器)處理。工具生成代表檔案邏輯分組的乙個pe32(+)檔案。實際發生的事情是,這個pe32(+)檔案包含乙個名為清單(manifest)的資料塊。清單也是元資料表的集合。這些表描述了構成程式集的檔案、程式集中 的檔案所實現的公共匯出的型別(就是程式集中定義的public型別,他們在程式集內外部均可見)以及與程式集關聯的資源或資料檔案。

在程式集的模組中,還包含與引用的程式集有關的資訊(包含他們的版本號)。這些資訊使程式集能夠自描述(self-describing)。也就是說,clr能判斷為了執行程式集中的**,程式集的直接依賴物件(immediate dependenct)是什麼。不需要在登錄檔或者adds中儲存額外的資訊。由於無需額外資訊,所以和非託管元件相比,程式集更容易部署。

生成的每個程式集可以是可執行引用程式,也可以是dll(其中含有一組由可執行程式使用的型別)。當然,最終是由clr管理這些程式集中的**的執行。這意味著目標及其必須安裝好.net framework。

如前所述,託管程式集同時包含元資料和il。il是與cpu無關的及其語言,是microsoft在請教了很多商業及學術性語言/編譯器的作者之後,費盡心思開發出來的。il比大多數cpu及其語言都高階。il能訪問和操作物件型別,並提供了指令來建立和初始化物件、呼叫物件上的虛方法以及直接運算元據元素。甚至提供了丟擲和捕捉異常的指令來實現錯誤處理。可將il視為一種物件導向的機器語言。

為了執行方法,首先必須把方法的il轉換成本機(native)cpu指令。這是clr的jit(just-in-time)編譯器的職責。下圖展示了方法首次呼叫時的執行過程。

就在main方法執行之前,clr會檢查出main的**引用的所有型別。這導致clr分配乙個內部資料結構來管理對引用型別的訪問。如上圖中的main方法引用了乙個console型別,導致clr分配乙個內部結構。在這個內部資料結構中,console型別定義的每個方法都有乙個對應的記錄項。每個記錄項都含有乙個位址,根據此位址即可找到方法的實現。對這個結構初始化時,clr將每個記錄項都指向包含在clr內部的乙個未編檔函式(jitcompiler)。

main方法首次呼叫writeline時,jitcompiler函式會被呼叫。jitcompiler函式負責將方法的il**編譯成本機cpu指令。由於il是「即時」(just in time)編譯的,所以通常將clr的這個元件稱為jit編譯器。

方法僅在首次呼叫時才會有一些效能損失。以後對該方法的所有呼叫都以本機**的形式全速執行,無需重新驗證il並把它編譯成本機**。

jit編譯器將本機cpu指令儲存到動態記憶體中。這意味著一旦應用程式終止,編譯好的**也會被丟棄。所以,將來再次執行應用程式,或者同時啟動應用程式的兩個例項(使用兩個不同的作業系統程序),jit編譯器必須再次將il編譯成本機指令。對於某些應用程式,這可能顯著增加記憶體消耗。

4.1  il和驗證

il基於棧。這意味著它的所有指令都要將運算元壓入(push)乙個執行棧,並從棧彈出(pop)結果。由於il沒有提供操作暫存器的指令,所以人們可以很容易地建立新的語言和編譯器,生成面向clr的**。

il指令還是「無型別」(typeless)的。例如,il提供了add指令將壓入棧的最後兩個運算元加到一起。add指令不分32位和64位版本。add指令執行時,他判斷棧中的運算元的型別,並執行恰當的操作。

il最大的優勢不是他對底層cpu的抽象,而是應用程式的健壯性和安全性。將il編譯成本機cpu指令時,clr執行乙個名為驗證的過程。這個過程會檢查高階il**,確定**所做的一切都是安全的。例如,會核實呼叫的每個方法都有正確數量的引數,傳給每個方法的每個引數都有正確的型別,每個方法的返回值都得到了正確的使用,每個方法都有乙個返回語句等等。託管模組的元資料報含驗證過程要用到的所有方法及型別資訊。

windows的每個程序都有自己的虛擬位址空間。獨立位址空間之所以必要,是因為不能簡單地新人乙個應用程式的**。應用程式完全可能讀寫無效的記憶體位址(令人遺憾的是,這種情況時有發生)。將每個windows程序都放到獨立的位址空間,將獲得健壯性與穩定性;乙個程序干擾不到另乙個程序。

然而,通過驗證託管**,可以確保**不會不正確地訪問記憶體,不會干擾到另乙個應用程式的**。這樣就可以放心地將多個託管應用程式放到同乙個windows虛擬位址空間執行。

由於windows程序需要大量作業系統資源,所以程序數量太多,會損害效能並制約可用的資源。用乙個程序執行多個應用程式,可減少程序數,從而增強效能,減少所需的資源,健壯性也沒有絲毫下降。這是託管**相較於非託管**的另乙個優勢。

4.2  不安全的**

c#編譯器預設生成安全**,這種**的安全性可以驗證。然而,c#編譯器也允許開發人員寫不安全的**。不安全**允許直接操作記憶體位址,並可操作這些位址處的位元組。這是乙個非常強大的功能,通常只有在與非託管**進行互操作,或者在提公升對效率要求極高的乙個演算法的效能的時候,才需要這樣做。(不安全**的所有方法都要用unsafe關鍵字標記)

25執行緒基礎 CLR

由clr via c 第三版 摘抄記錄.1 執行緒是cpu的虛擬化,windows為每個程序提供專用線程 cpu 2 執行緒開銷 記憶體和時間。執行緒核心物件 os為系統中建立的每個執行緒都分配並初始化這種資料結構之一。其中包含對執行緒進行描述的屬性,和上下文。上下文是記憶體塊,x86的是約700位...

CLR 設計型別

clr 設計型別 前言好記性不如爛 筆頭 系列。目錄型別基礎 基元型別 引用型別和值型別 型別與成員 常量與字段 方法型別基礎 執行時 要求每個型別最終都從system.object 型別派生。由於所有型別最終都從system.object 派生,所以可以保證每個型別的每個物件都有一組最基本的方法。...

clr 元資料

clr相關編譯器編譯生成的託管模組由四部分組成 pe32或32 頭 clr頭 元資料 il 元資料和il 完全對應,保持一致 性。元資料有很多用途 vs的智慧型感知,自動補全 驗證保證型別安全 序列化 反序列化 垃圾 從元資料得知哪些根引用了物件 元資料報含兩類表,一種描述源 中定義的型別和成員 另...