C#技術漫談之公共語言運行庫(CLR)

作者: spring yang  來源: 博客園  發布時間: 2011-09-22 10:23  閱讀: 7119 次  推薦: 8   原文鏈接   [收藏]  
摘要:本文介紹的是C#開發中公共語言運行庫(CLR),這些原理性質的知識能讓大家更好的開發C#程序。

  概述

  .NET Framework的核心是其運行庫的執行環境,稱為公共語言運行庫(CLR)或.NET運行庫。通常將在CLR的控制下運行的代碼稱為托管代碼(managed code)。

  但是,在CLR執行編寫好的源代碼之前,需要編譯它們(在C#中或其它語言中)。在.NET中,編譯分為兩個階段:

  1、把源代碼編譯為Microsoft中間語言(IL)。

  2、CLR把IL編譯為平臺專用的代碼。

  這個兩階段的編譯過程非常重要,因為Microsoft中間語言(托管代碼)是提供.NET的許多優點的關鍵.

  .NET平臺的整體結構:

.NET平臺的整體結構

  .NET Framework是架構在Windows平臺上的一個虛擬的運行平臺,你可以想象將最下層Windows換做其他的操作系統,例如說Linux, 一樣可以實現使用符合CLS(Common Language Specification, 通用語言規范)的.NET語言(VB.NET、C#、JScript.NET等)來創建ASP.NET或Windows Form(可能會叫Linux Forms)應用程序的功能,這其實就是Mono計劃要實現的功能。所以可以這么認為,理論上,C#是一種可以跨平臺的語言,這很象Java。

  C#另一個比較象Java的地方是,它也是一種(特殊意義上的)語言,同Java一樣,C#編寫的程序代碼也是先通過C#編譯器編譯為一種特殊的字節代碼 (Microsoft Intermediate Language, MSIL,微軟中間語言),運行時再經由特定的編譯器(JIT編譯器)編譯為機器代碼,以供操作系統執行。

  不僅是C#語言,所有.NET語言(將會包括我們常用的幾十種現代的編碼語言)都可以編寫面向CLR的程序代碼,這種代碼在.NET中被稱為托管代碼(Managed Code),所有的Managed Code都直接運行在CLR上,具有與平臺無關的特性。

  解釋性的語言很安全,并且可以通過他的運行平臺為其賦予更多的功能,例如自動內存管理,異常處理等。

  CLR結構圖

CLR結構圖

  C#所具有的許多特點都是由CLR提供的,如類型安全(Type Checker)、垃圾回收(Garbage Collector)、異常處理(Exception Manager)、向下兼容(COM Marshaler)等,具體的說,.NET上的CLR為開發者提供如下的服務:

  1、平臺無關:CLR實際上是提供了一項使用了虛擬機技術的產品,它構架在操作系統之上,并不要求程序的運行平臺是 Windows系統,只要是能夠支持它的運行庫的系統,都可以在上面運行.NET應用。所以,一個完全由托管代碼組成的應用程序,只要編譯一次,就可以在任何支持.NET的平臺上運行。

  2、跨語言集成:CLR允許以任何語言進行開發,用這些語言開發的代碼,可以在CLR環境下緊密無縫的進行交叉調用,例如,可以用VB聲明一個基類對象,然后在C#代碼中直接創建次基類的派生類。
  3、自動內存管理:CLR提供了垃圾收集機制,可以自動管理內存。當對象或變量的生命周期結速后,CLR會自動釋放他們所占用的內存.
  4、跨語言應用

  當編程人員在用自己喜歡的編程語言寫源代碼的時候, 這個源代碼在被轉化成媒介語言(IL)之前,先被編譯成了一個獨立的可執行單元(PE)。這樣無論你是一個VB.NET程序員,或一個C#程序員,甚至是使用托管的C++的程序員。只要被編譯成IL就是同等的。 首先,編譯輸出的exe是一個由中間語言(IL),元數據(Metadata)和一個額外的被編譯器添加的目標平臺的標準可執行文件頭(比如Win32平臺就是加了一個標準Win32可執行文件頭)組成的PE(portable executable,可移植執行體)文件,而不是傳統的二進制可執行文件 —— 雖然他們有著相同的擴展名。

  中間語言是一組獨立于CPU的指令集,它可以被即時編譯器Jitter翻譯成目標平臺的本地代碼。中間語言代碼使得所有Microsoft.NET平臺的高級語言C#, VB.NET, VC.NET等得以平臺獨立,以及語言之間實現互操作。元數據是一個內嵌于PE文件的表的集合。
  5、版本控制

  由于使用了元數據,所以你可以使用XCOPY簡單的復制就可以了,而CLR也可以在運行時期讀取元數據,以確保多版本程序運行在同一進程中。使用公共語言運行庫的程序集的所有版本控制都在程序集級別上進行。一個程序集的特定版本和依賴程序集的版本在該程序集的清單中記錄下來。除非被配置文件(應用程序配置文件、發行者策略文件和計算機的管理員配置文件)中的顯式版本策略重寫,否則運行庫的默認版本策略是,應用程序只與它們生成和測試時所用的程序集版本一起運行。
  6、.NET安全

  .NET提供了一組安全方案。負責進行代碼的訪問安全性檢查。允許我們對保護資源和操作的訪問。代碼需要經過身份確認和出處鑒別后才能得到不同程度的信任。安全策略是一組可配置的規則,公共語言運行庫在決定允許代碼執行的操作時遵循此規則。安全策略由管理員設置,并由運行庫強制。

  運行庫確保代碼只能訪問安全策略允許的資源和調用安全策略允許的代碼。 每當發生加載程序集的嘗試時,運行庫就使用安全策略確定授予程序集的權限。在檢查了描述程序集標識的信息(稱為證據)后,運行庫使用安全策略決定代碼的信任程度和由此授予程序集的權限。證據包括但不僅限于代碼的出版商、它的站點以及它的區域。安全策略還確定授予應用程序域的權限。
  7、簡單的組件互操作性。
  8、自描述組件:
自描述組件是指將所有數據和代碼都放在一個文件中的執行文件。自描訴組件可以大大簡化系統的開發和配置,并且改進系統的可靠性。

  通用語言運行時(CommonLanguageRuntiome, CLR)最早被稱為下一代Windows服務運行時(NGWS Runtime)。它是直接建立在操作系統上的一個虛擬環境,主要的任務是管理代碼的運行。CLR現在支持幾十種現代的編程語言為它編寫代碼,然后以一種中間語言(Intermediate Langeoage, IL)代碼的形成被執行。并且,CLR還提供了許多功能以簡化代碼的開發和應用配置,同時也改善了應用程序的可靠性。如你所知,如果某種語言的編譯器是以運行時為目標的,那么利用該語言開發生成的代碼在.NET中被稱為托管代碼,因為這樣的代碼是直接運行在CLR上的,所以具有與平臺無關的特點。

  在.NET平臺結構圖中,CLR的上面是.NET的基類庫,這組基類庫包括從基本輸入輸出到數據訪問等各方面,提供了一個統一的面向對象的,層次化的,可擴展的編程接口。從.NET平臺結構圖中也可以看到,基類庫可以被各種語言調用和擴展,也就是說不管是C#,VB.NET還是VC++.NET,都可以自由的調用.NET的類庫,因為C#自身只有77個關鍵字,而且語法對程序員來說無需費工夫學習。 BCL則相反,它包含了4500個以上的類和無數的方法、屬性,在你的C#程序中隨時都可能會用到它來完成自己的任務。

  還有一個很重要的概念你需要明白,這就是公共語言架構(Common Language Infrastructure, CLI). CLI是CLR的一個子集,也就是.NET中最終對編譯成MSIL代碼的應用程序的運行環境進行管理的那一部分。在CLR結構圖中CLI位于下半部分,主要包括類加載器(Class Loader)、實時編譯器(IL To Native Compilers)和一個運行時環境的垃圾收集及將使用任何語言編寫的代碼,通過其特定的編譯器轉換為MSIL代碼之后運行其上,甚至還可以自己寫 MSIL在CLI上運行。

  9、調用和配置

  當運行庫試圖解析對另一個程序集的引用時,就開始進行定位并綁定到程序集的進程。該引用可以是靜態的,也可以是動態的。在生成時,編譯器在程序集清單的元數據中記錄靜態引用。動態引用是由于調用各種方法而動態構造的,例如 System.Reflection.Assembly.Load 方法。 引用程序集的首選方式就是使用完全引用,包括程序集名稱、版本、區域性和公鑰標記(如果存在)。

  運行庫就會使用這些信息來定位程序集。無論是對靜態程序集的引用還是對動態程序集的引用,運行庫均使用相同的解析過程。 還可通過向調用方法僅提供有關程序集的部分信息的方式(例如僅指定程序集名稱),對程序集進行動態引用。在這種情況下,僅在應用程序目錄下搜索程序集,不進行其他檢查。您可以使用不同加載程序集方法中的任何方法(例如 System.Reflection.Assembly.Load 或 AppDomain.Load)進行部分引用。如果希望運行庫在全局程序集緩存和應用程序目錄下檢查引用的程序集,可以用 System.Reflection.Assembly.LoadWithPartialName 方法指定部分引用。

  最后,可以使用諸如 System.Reflection.Assembly.Load 之類的方法進行動態引用并只提供部分信息;然后在應用程序配置文件中用 <qualifyAssembly> 元素限定該引用。該元素使您可以在應用程序配置文件中而不是在代碼中提供完全引用信息,包括名稱、版本、區域性和公鑰標記(如果適用)。如果要在應用程序目錄外完全限定對某個程序集的引用,或者如果要引用全局程序集緩存中的程序集,但又希望方便地在配置文件中而不是在代碼中指定完全引用,就可以采用這一技術。

  10、GC

  一個跟蹤過程,它傳遞性地跟蹤指向當前使用的對象的所有指針,以便找到可以引用的所有對象,然后重新使用在此跟蹤過程中未找到的任何堆內存。公共語言運行庫垃圾回收器還壓縮使用中的內存,以縮小堆所需要的工作空間。 垃圾收集器的基本算法很簡單: ● 將所有的托管內存標記為垃圾 ● 尋找正被使用的內存塊,并將他們標記為有效 ● 釋放所有沒有被使用的內存塊 ● 整理堆以減少碎片。

  CLR的基本特性

  1、與本機代碼無關 - MSIL (中間語言)

  2、讓我們使用同一種語言 - CLR (公共語言運行時)

  3、我們手中的零件 - Assembly (裝配件)

  4、讓我們在同一個系統中運行 - CTS (通用類型系統)

  5、宇宙大爆炸后的產物 - metadata (元數據)

  6、讓我們的語言可以交流 - CLS (公共語言系統)

  7、在動態中交互 - Reflection (反射)

  8、屬于我們自己的空間 - NameSpace (名稱空間)

MSIL:微軟中間語言

Reflection:反射

Metadata:元數據

PE:可執行可移植文件

Assembly: 程序集(裝配件)

NameSpace:名稱空間

CTS:通用類型系統

GC(Garbage Colection) :無用單元回收

CLR:公共語言系統

Attribute:屬性(注意不要和Property混淆)

Boxing: 裝箱

UnBoxing: 拆箱

  MSIL 使用.NET支持的語言所編寫的代碼

  MSIL(Microsoft Intermediate Language)微軟的中間語言。和JAVA的虛擬機類似,是與CPU無關的指令集。當編譯為托管代碼時,編譯器將源代碼翻譯為MSIL, 如上圖所示。MSIL包括用于加載、存儲和初始化對象以及對對象調用方法的指令,還包括用于算術和邏輯運算、控制流、直接內存訪問、異常處理和其他操作的指令。在可以執行代碼前,必須將 MSIL 轉換為 CPU 特定的代碼,這通常是通過實時 (JIT) 編譯器完成的。由于公共語言運行庫為它支持的每種計算機結構都提供了一種或多種 JIT 編譯器,因此可以在任何受支持的結構上對同一組 MSIL 進行 JIT 編譯和執行。這樣總結上面的就是:中間語言是一組獨立于CPU的指令集,它可以被即時編譯器Jitter翻譯成目標平臺的本地代碼。

  PE

  Windows PE和一個 .NET PE的主要區別在于Windows PE是由操作系統執行的,而 .NET PE 卻被轉變成為.NET Framework的CLR. 識別一個PE是 .NET還是Windows取決于他的通用的目標文件格式 (COFF) 是否使用Windows的操作系統。目標文件格式 (COFF) 指定了任何文件都分成兩個部分:文件數據本身以及描述文件內包含的數據內容的頭文件串。MSIL 匯編程序從 MSIL 匯編語言生成可移植可執行的 (PE) 文件。可以運行結果可執行文件(該文件包含 MSIL 和所需的元數據)以確定 MSIL 是否按預期執行。這就是我為什么會談到PE。

  那么PE文件是怎么執行的呢?下面是一個典型的.NET應用程序的執行過程:

  1. 用戶執行編譯器輸出的應用程序(PE文件),操作系統載入PE文件,以及其他的DLL(.NET動態連接庫)。
  2. 操作系統裝載器根據PE文件中的可執行文件頭跳轉到程序的入口點。顯然,操作系統并不能執行中間語言,該入口點也被設計為跳轉到mscoree.dll(.NET平臺的核心支持DLL)的_CorExeMain()函數入口。
  3. CorExeMain()函數開始執行PE文件中的中間語言代碼。這里的執行的意思是CLR(通用語言運行時)按照調用的對象方法為單位,用JIT(即時編譯器)將中間語言編譯成本地機二進制代碼,執行并根據需要存于機器緩存。
  4. 程序的執行過程中,GC(垃圾收集器)負責內存的分配,釋放等管理功能。
  5. 程序執行完畢,操作系統卸載應用程序。

  .NET Framework 環境

  下面的插圖顯示公共語言運行時和類庫與應用程序之間以及與整個系統之間的關系。該插圖還顯示托管代碼如何在更大的結構內運行。

  公共語言運行時(CLR)的功能

  公共語言運行時管理內存、線程執行、代碼執行、代碼安全驗證、編譯以及其他系統服務。這些功能是在公共語言運行時上運行的托管代碼所固有的。

  • 至于安全性,取決于包括托管組件的來源(如 Internet、企業網絡或本地計算機)在內的一些因素,托管組件被賦予不同程度的信任。

  • 運行時強制實施代碼訪問安全。例如,用戶可以相信嵌入在網頁中的可執行文件能夠在屏幕上播放動畫或唱歌,但不能訪問他們的個人數據、文件系統或網絡。這樣,運行時的安全性功能就使通過 Internet 部署的合法軟件能夠具有特別豐富的功能。

  • 運行時還通過實現稱為常規類型系統 (CTS) 的嚴格類型驗證和代碼驗證基礎結構來加強代碼可靠性。CTS 確保所有托管代碼都是可以自我描述的。各種 Microsoft 編譯器和第三方語言編譯器都可生成符合 CTS 的托管代碼。這意味著托管代碼可在嚴格實施類型保證和類型安全的同時使用其他托管類型和實例。

  • 此外,運行時的托管環境還消除了許多常見的軟件問題。例如,運行時自動處理對象布局并管理對對象的引用,在不再使用它們時將它們釋放。這種自動內存管理解決了兩個最常見的應用程序錯誤:內存泄漏和無效內存引用。

  • 運行時還提高了開發人員的工作效率。例如,程序員可以用他們選擇的開發語言編寫應用程序,卻仍能充分利用其他開發人員用其他語言編寫的運行時、類庫和組件。任何選擇以運行時為目標的編譯器供應商都可以這樣做。以 .NET Framework 為目標的語言編譯器使得用該語言編寫的現有代碼可以使用 .NET Framework 的功能,這大大減輕了現有應用程序的遷移過程的工作負擔。

  • 盡管運行時是為未來的軟件設計的,但是它也支持現在和以前的軟件。托管和非托管代碼之間的互操作性使開發人員能夠繼續使用所需的 COM 組件和 DLL。
  • 運行時旨在增強性能。盡管公共語言運行時提供許多標準運行時服務,但是它從不解釋托管代碼。一種稱為實時 (JIT) 編譯的功能使所有托管代碼能夠以它在其上執行的系統的本機語言運行。同時,內存管理器排除了出現零碎內存的可能性,并增大了內存引用區域以進一步提高性能。
  • 最后,運行時可由高性能的服務器端應用程序(如 Microsoft SQL Server 和 Internet 信息服務 (IIS))承載。此基礎結構使您在享受支持運行時承載的行業最佳企業服務器的優越性能的同時,能夠使用托管代碼編寫業務邏輯。

  公共語言運行時(CLR)細節

  • 若要使公共語言運行時能夠向托管代碼提供服務,語言編譯器必須生成一些元數據來描述代碼中的類型、成員和引用。元數據與代碼一起存儲;每個可加載的公共語言運行時可遷移執行 (PE) 文件都包含元數據。公共語言運行時使用元數據來完成以下任務:查找和加載類,在內存中安排實例,解析方法調用,生成本機代碼,強制安全性,以及設置運行時上下文邊界。
  • 公共語言運行時自動處理對象布局并管理對象引用,當不再使用對象時釋放它們。按這種方式實現生存期管理的對象稱為托管數據。垃圾回收消除了內存泄漏以及其他一些常見的編程錯誤。如果您編寫的代碼是托管代碼,則可以在 .NET Framework 應用程序中使用托管數據、非托管數據或者同時使用這兩種數據。由于語言編譯器會提供自己的類型(如基元類型),因此您可能并不總是知道(或需要知道)這些數據是否是托管的。
  • 有了公共語言運行時,就可以很容易地設計出對象能夠跨語言交互的組件和應用程序。也就是說,用不同語言編寫的對象可以互相通信,并且它們的行為可以緊密集成。例如,可以定義一個類,然后使用不同的語言從原始類派生出另一個類或調用原始類的方法。還可以將一個類的實例傳遞到用不同的語言編寫的另一個類的方法。這種跨語言集成之所以成為可能,是因為基于公共語言運行時的語言編譯器和工具使用由公共語言運行時定義的常規類型系統(CTS),而且它們遵循公共語言運行時關于定義新類型以及創建、使用、保持和綁定到類型的規則。
  • 所有托管組件都帶有生成它們所基于的組件和資源的信息,這些信息構成了元數據的一部分。公共語言運行時使用這些信息確保組件或應用程序具有它需要的所有內容的指定版本,這樣就使代碼不太可能由于某些未滿足的依賴項而發生中斷。注冊信息和狀態數據不再保存在注冊表中(因為在注冊表中建立和維護這些信息很困難)。取而代之的是,有關您定義的類型(及其依賴項)的信息作為元數據與代碼存儲在一起,這樣大大降低了組件復制和移除任務的復雜性。
  • 語言編譯器和工具公開公共語言運行時的功能的方式對于開發人員來說不僅很有用,而且很直觀。這意味著,公共語言運行時的某些功能可能在一個環境中比在另一個環境中更突出。您對公共語言運行時的體驗取決于所使用的語言編譯器或工具。例如,如果您是一位 Visual Basic 開發人員,您可能會注意到:有了公共語言運行時,Visual Basic 語言的面向對象的功能比以前多了。

  .NET Framework 類庫

  • .NET Framework 類庫是一個與公共語言運行時緊密集成的可重用的類型集合。該類庫是面向對象的,并提供您自己的托管代碼可從中導出功能的類型。這不但使 .NET Framework 類型易于使用,而且還減少了學習 .NET Framework 的新功能所需要的時間。此外,第三方組件可與 .NET Framework 中的類無縫集成。
8
0
 
標簽:C# .NET CLR
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()