天天看點

淺談CLR

1.什麼是CLR

  CLR(Common Language Runtime)公共語言運作時,是一個可由多種程式設計語言使用的“運作時”。CLR的核心功能(比如記憶體管理、程式集加載、安全性、異常處理和線程同步)可由面向CLR的所有語言使用。CLR不關心開發人員使用哪種語言進行程式設計,隻要編譯器面向CLR就可以了,所有,開發人員應該使用自己最适合和熟悉的語言進行程式設計。所有的程式設計語言在面向CLR編譯器的編譯都生成了一個托管子產品。托管子產品是一個标準的32位的Microsoft Windows可移植執行體(PE32)檔案,或者是一個标準的64位Windows可移植的PE32+檔案,他們都需要CLR才能執行。

2.托管子產品的各個組成部分

  PE32或PE32+頭 标準Windows PE檔案頭,類似于“公共對象檔案格式”。

  CLR頭 包含使用這個子產品成為一個托管子產品的資訊(可由CLR和一些實用程式進行解釋)。頭中包含了需要的CLR版本,一些标志,托管子產品人口方法(Main方法)的MethodDef中繼資料标記(token),以及子產品的中繼資料、資源、強名稱、一些flag以及其他不太重要的資料項的位置/大小

  中繼資料 每個托管子產品都包含中繼資料表。主要有兩種類型的表:一種類型的表描述源代碼中定義的類型和成員:另一種類型的表描述源代碼引用類型和成員

  IL(中間語言)代碼 編譯器編譯源代碼時生成的代碼。在運作時,CLR将将IL編譯成本地的CPU指令

中繼資料的用途:

  編譯時,中繼資料消除了對本地C/C++頭和庫檔案的需求,因為在負責實作類型/成員的IL代碼中,已包和引用的類型/成員有關的全部資訊。編譯器可直接從托管子產品讀取中繼資料

  Microsoft Visual Studio 使用中繼資料幫助你寫代碼。也就是“智能感覺(IntelliSense)技術”可以解析中繼資料,指出一個類型提供了那些方法、屬性、事件和字段等等。

  CLR的代碼驗證過程使用中繼資料確定代碼隻執行“類型安全”的操作。

  中繼資料允許将一個對象的字段序列化到一個記憶體中,将其發送給另一台機器,然後反序列化,在遠端機器上重建對象的狀态

  中繼資料允許垃圾回收器跟蹤對象的生存期。垃圾回收器能判斷任何對象的類型,并從中繼資料知道那個對象中的哪些字段引用了其他對象。

3.程式集

  其實,CLR不和托管子產品一起工作。它和程式集(assembly)一塊工作。程式集是一個或多個子產品/資源檔案的邏輯分組。程式集是重用、安全性已經版本控制的最小單元。程式集是自描述的(self-describing)

4 執行程式集的代碼

  托管程式集同時包含中繼資料和IL。為了執行程式,首先必須把它的IL轉換成本地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的這個元件稱為JITter或者JIT編譯器。

  JITCompiler函數被調用時,它知道要調用的是哪個方法,以及具體是什麼類型定義了該方法。然後,JITCompiler會在定義程式集的中繼資料中查找被調用的方法的IL。接着,JITCompiler驗證IL代碼,并将IL代碼編譯成本地CPU指令。本地CPU指令被儲存到一個動态配置設定的記憶體塊中。然後,JITCompiler傳回CLR為類型建立的内部資料結構,找到與被調用的方法對象的那一條記錄,修改最初對JITCompiler的引用,讓它現在指向記憶體塊中的位址。最後,JITCompiler函數跳轉到記憶體塊中的代碼。

  第二次調用WriteLine。這一次,由于對WriteLine的代碼進行了驗證和編譯,是以直接執行記憶體塊中的代碼,完全跳過JITCompiler函數。

第二次調用WriteLine的情況

 5.通用類型系統

  為了通過類型,用一種程式設計語言寫的代碼能與用另一種語言寫的代碼溝通,Microsoft指定了一正式的規範,即“通用類型系統”(Common Type System,CTS),它描述了類型的定義和行為。

  CTS規範規定,一個類型可以包含零個或多個成員。

  字段(Field) 一個資料變量

  方法(Method) 一個函數

  屬性(Property) 對于調用者,該成員看起來像是一個字段

  事件(Event) 事件在對象以及其他相關對象之間實作了一通用機制。

  CTS 還指定了類型可視性規則以及類型成員的通路規則,例如private,family等

  CTS還為類型繼承。虛方法、對象生存期等定義了相應的規則

  特比說一下CTS中的一條規則:所有類型最終必須從預定義的System.Object類型繼承。System.Object可以做的事情如下:

  比較兩個執行個體的相等性

  擷取執行個體的哈希碼

  查詢一個執行個體的真正類型

  執行執行個體的淺拷貝

  擷取視執行個體對象的目前狀态的一個字元串表示

6.公共語言規範

  為了建立很容易從其他程式設計語言中通路的類型,隻能從自己的程式設計語言中挑選其他所有語言都确定支援的那些功能,Microsoft定義了一個“公共語言規範”(Common Language Specifiaction,CLS),它詳細定義了一個最小功能集。

 7.中繼資料

  上面已經提到托管的PE檔案由4個部分構成:PE32(+)頭、CLR頭、中繼資料以及IL。

  這裡我們主要說一下中繼資料。

  中繼資料是一個二進制資料塊,由幾個表構成。這些表分為三個類别:定義表(definiton talbe)、引用表(reference table)和清單表(mainfest table)。

  常用中繼資料定義表(編譯器編譯源代碼時,代碼定義的任何一樣東西都會導緻定義表中的表中建立一個記錄項):

  ModuleDef 總是包含一個用于标示子產品的記錄項。

  TypeDef 子產品中定義的每個類型都在這個定義表中有一個對應的記錄項。

  MethodDef 子產品中定義的每個方法都在這個定義表中有一個對應的記錄項。

  FieldDef 子產品中定義的每個字段都在這個定義表中有一個對應的記錄項

  ParamDef 子產品中定義的每個參數都在這個定義表中有一個對應的記錄項

  PropertyDef 子產品中定義的每個屬性都在這個定義表中有一個對應的記錄項

  EventDef 子產品中定義的每個事件都在這個定義表中有一個對應的記錄項

   常用的引用中繼資料表:

  AssemblyRef 子產品中引用的每個程式集在這個表中都有一個對應的記錄項

  ModuleRef 子產品引用的每個類型可能是由别的PE子產品實作的,所有那些子產品在這個表都有一個記錄項

  TypeDef 子產品引用的每個類型在這個表中都有一個對應的記錄項

  MemberRef 子產品引用的每個成員都在這個表中有一個對應的記錄項

  清單中繼資料表:

  AssemblyDef 如果該子產品标示的是一個程式集,就在這個中繼資料表中包含單個記錄項。該記錄項列出了程式集名稱(不含路徑和擴充名)、版本(major,minor,build和revision)、語言文化(culture)、一些标志(flag)、雜湊演算法以及釋出者的公鑰。

  FileDef 作為程式集一部分的每個PE檔案和資源檔案在這個表中都有一個對應的記錄項。

  MainifestResourceDef 作為程式集一部分的每個資源在這個表中都有一個對應的記錄項

  ExportedTypesDef 從程式集的所有PE子產品中導出的每個public類型中在這個表中都有一個對應的記錄項。

下一篇: NFS rhel 7

繼續閱讀