clr基本python
CLR(Common Language Runtime)是一個可由多種編程語言使用的「運行時」。(例如:c#,c++/cli,vb,f#,ironpython,ironruby,il...)c++
CLR的核心功能內存管理、程序集加載、安全性、異常處理、線程同步、泛型、尾調用指令和基本的公共語言基礎結構 (CLI) 類型系統等。web
託管模塊是一個標準的32位microsoft windows可移值執行體pe32文件(64位系統爲pe32+),他們須要clr才能執行。算法
託管的程序集老是利用windows的數據執行保護和地址空間佈局隨機化,這2個功能旨在加強整個系統的安全性。編程
託管模塊的組成部分:pe32(或pe32+)頭,clr頭,元數據,il中間代碼。 c#
本地代碼編譯器生成的是面向特定cpu架構的代碼。相反每一個面向clr的編譯器生成的都是il代碼。windows
源代碼文件----》編譯器----》託管模塊緩存
加載CLR安全
.net framework sdk提供了名爲clrver.exe的命令行使用程序,它能列出一臺機器上安裝的全部clr版本。ruby
c#編譯器能夠指定一個平臺(基於x86 windows,x64 windows或者ia64 windows).net4.0以前默認anycpu,4.0 exe項目默認x86
若是一個非託管應用程序調用loadlibrary來加載一個託管程序集,windows會自動加載並初始化clr,以便處理程序集中的代碼。
執行clr
託管程序集同時包含元數據和il。il是與cpu無關的機器語言。il比大多數cpu機器語言都要高級。il也能使用匯編語言來寫。
爲了執行一個方法,首先必須把它的il轉換爲本地cpu指令。這是clr的jit編譯器的職責。
首次執行託管exe---->jitcompiler---->查找方法、從元數據中獲取il、分配內存塊、編譯cpu指令、修改方法對應的記錄項、跳轉內存塊中的本地代碼
第二次執行託管exe跳過jitcompiler直接跳轉內存塊中的本地代碼
對於大多數應用程序,因jit編譯形成的性能損失並不顯著,並且clr的jit編譯器會對本地代碼進行優化,優化後的代碼會得到更出色的性能。
c#調試產生的pdb文件就是幫助調試器查找局部變量並將il指令映射到源代碼。
release版本就是讓發佈程序通過jit優化,因此線上項目最好都以release版本發佈
clr提供一個在操做系統進程中執行多個託管應用程序的能力。每一個託管的應用程序都在一個appdomain中執行。
IL
IL是基於棧的。
微軟提供ilasm.exe的il彙編器和一個名爲ildasm.exe的il反彙編器。
IL指令是無類型的(typeless)
IL最大的優點並不在於它對底層CPU的抽象,而是應用程序的健壯性和安全性。
FramWork
framework class library是一組dll程序集的名稱,其中包含數千個類型定義,每一個類型都公開了一些功能。
通用類型系統(common type system)描述了類型的定義和行爲。一個類型能夠包含另個或者多個成員。
公共語言運行規範(common language specification)詳細定義了一個最小功能集。任何編譯器生成的類型要向兼容於由其餘符合cls面向clr的語言所生成的組件,就必須支持這個最小功能集
響應文件
響應文件是一個文本,其中包含了一組編譯器命令行開關。執行csc.exe時,編譯器會打開響應文件,並使用其中包含的全部開關,感受就像是這些開關直接在命令行傳遞給csc.exe。
響應文件能帶來不少方便,由於沒必要每次在編譯項目時,都手動指定須要的命令行參數。
c#編譯器容許同時指定多個響應文件。
程序集
程序集是一個抽象的概念,clr實際上不和模塊一塊兒工做,而是和程序集一塊兒。
程序集是一個或多個模塊/資源文件的邏輯分組。是重用、安全性以及版本控制的最小單元。在clr中至關於一個「組件」
託管模塊(il+元數據)+資源文件 ----》編譯器----》程序集
默認狀況,編譯器會把生成的託管模塊轉換成程序集。
對於一個可重用的、可保護的、可版本控制的組件,程序集把它的邏輯表示和物理表示區分開。
在程序集的模塊中還包含與引用的程序集有關的信息(例如版本號)。這些信息使程序集能夠自描述。換句話說,clr能判斷出爲了執行程序集中的代碼,程序集中得依賴對象是什麼。不須要在註冊表或active directory domain services中保存額外的信息。
程序集的全部文件中,有一個文件容納了清單。清單也是一組元數據表的集合,表中主要包含了做爲程序集的組成部分的文件的名稱。
可用單獨的文件對類型進行劃分、可在本身的程序集中添加資源或者數據文件、程序集包含的各個類型能夠用不一樣的編程語言來實現。
元數據
元數據是一組數據表。其中的一些數據表描述可模塊中定義的內容,好比類型及成員。還有一些元數據描述了託管模塊引用的內容,好比導入的類型及成員。
元數據是一些老技術的超集,這些老技術包括com的「類型庫」和「接口定義語言」文件。
元數據老是嵌入和代碼相同exe/dll文件中。
元數據的用途:編譯時元數據消除對本地c/c++頭和庫文件的要求,vs使用元數據幫助你智能感知代碼,類型安全驗證,序列化,gc管理。
元數據是一個二進制數據塊,由幾個表構成。這些表分爲3個類別:定義表、引用表和清單表。
定義表:moduledef,typedef,methoddef,fielddef,paramdef,propertydef,eventdef
引用表:assemblyref,mouduleref,typeref,memberref
清單表:assemblydef,filedef,manifestresourcedef,exportedtypesdef
程序集還講語言文化做爲其身份標識的一部分。沒有指定具體語言文化的程序集稱爲語言文化中性。
部署
程序集的打包沒有任何特殊需求。不須要對註冊表進行任何修改,clr就能夠在應用程序的目錄中查找引用的程序集。
也可使用其餘機制來打包和安裝程序集文件,好比使用.cab文件。還能夠將程序集文件打包成一個msi。
msi文件可實現程序集的「按需安裝」,在clr首次嘗試加載程序集的時候才安裝這個程序集。
部署到和應用程序相同的目錄中得程序集稱爲私有部署的程序集,這是由於程序集文件不和其餘任何應用程序共享(除非其餘應用程序也部署到這個目錄中)。
clr嘗試定位一個程序集文件時,老是先在應用程序基目錄中查找。若是沒有找到,就會掃描幾個子目錄。
對於可執行應用程序(exe),配置文件必須在應用程序的基目錄中,並且必須採用exe文件的全名做爲文件名,再加一個.config擴展名
對於microsoft asp.net web窗體應用程序,文件必須在web應用程序的虛擬根目錄中,並且老是命名爲web.config
共享程序集與強命名程序集
弱命名程序集與強命名程序集都是c#編譯器或者al.exe產生。他們的區別在於強命名程序集使用發佈者的公鑰/私鑰對進行了簽名,它惟一性地標識了程序集的發佈者。
一個程序集能夠採起兩種方式來部署:私有或全局。弱命名程序集只能夠私有部署。
可使用sn.exe來生成公鑰/私鑰對。
全局程序集緩存
若是一個程序集要由多個應用程序訪問,必須把它放到一個已知的目錄中,並且clr在檢測到對該程序集的一個引用時,必須知道自動檢查該目錄。這個已知的位置成爲全局程序集緩存(GAC)。
GAC一般位於c:\windows\assembly目錄下(假定windows安裝到c:\windows目錄)
可使用GACUtil.exe在GAC中安裝一個強命名程序集
GAC目錄是結構化的:其中包含許多子目錄,並用一個算法來生成這些子目錄的名稱。
CLR解析引用類型
CLR先加載並初始化程序集,而後讀取clr頭,查找標識應用程序的入口方法的methoddeftoken。而後clr會檢索methoddef元數據表,找到該方法的il代碼在文件中的偏移量,把這些il代碼jit編譯成本地代碼。編譯時會對代碼進行驗證以確保其類型安全性。最後執行本地代碼。
clr能夠在三個地方找到類型:同一個文件(早起綁定)、不一樣的文件但同一個程序集(當前程序集清單目錄)、不一樣的文件不一樣的程序集(其餘程序集清單目錄)。
對於clr來講,全部程序集都是根據名稱、版本、語言文化和公鑰來標識的。
GAC根據名稱、版本、語言文化和cpu架構來標識程序集。
類型基礎
全部類型都是從system.object派生
system.object提供4個公共實例方法equals,gethashcode,tostring,gettype。
system.object提供2個受保護方法memberwiseclone,finalize.
clr要求全部對象都用new操做符來建立。
new操做符所做的事情:1.它計算類型及其全部基類型中定義的全部實例字段所需的字節數。堆上的每一個對象都須要一些額外的成員--「類型對象指針」和「同步塊索引」。這些成員由clr用於管理對象。這些額外成員的字節數會計入對象大小。2.它從託管堆中分配指定類型要求的字節數,從而分配對象的內存,分配的全部字節都設爲零。3.它初始化對象的--「類型對象指針」和「同步塊索引」成員。4.調用類型的實例構造器,向其傳入在對new的應用中指定的任何實參。
clr最重要的特性之一就是類型安全性。在運行時,clr總要知道一個對象是什麼類型。調用gettype方法,老是知道一個對象確切的類型是什麼。
clr容許將一個對象轉換成爲它的實際類型或者它的任何基類型。
使用c#的is和as操做符來轉型。
命名空間用於對相關的類型進行邏輯性分組,開發人員使用命名空間來方便地定位一個類型。
命名空間和程序集不必定是相關的。特別是同一個命名空間中的各個類型多是在不一樣的程序集中實現的。
一個線程建立時,會分配一個1mb大小的棧。這個棧的空間用於向方法傳遞實參,並用於方法內部定義的局部變量。棧是從高位內存地址向地位內存地址構建的。
windows進程啓動後,clr加載到其中,託管堆初始化,建立一個線程。當jit將方法的il代碼轉化成本地cpu指令時,會注意到方法內部引用的全部類型。在這個時候clr要確保定義了這些類型的全部程序集已加載。而後利用程序集的元數據,clr提取與這些類型有關的信息,並建立一些數據結構來標識類型自己。
堆上的全部對象都包含兩個額外的成員類型對象指針和同步塊索引。定義一個類型時,能夠在類型的內部定義靜態數據字段。爲這些靜態數據字段提供支援的字節是在類型對象自身中分配的。在每一個類型對象中,最後都包含一個方法表。在方法表中,類型定義的每一個方法都有一個對應的記錄項。
當clr肯定方法須要的全部類型對象都已建立,並且方法的代碼已經編譯以後,就容許線程開始執行方法的本地代碼。方法的「序幕」代碼執行時必須在線程棧中爲局部變量分配內存。
jit編譯器在類型對象的方法表中查找引用了被調用方法的記錄項,對方法進行jit編譯,再調用jit編譯的代碼。
system.object的gettype方法返回的是存儲在制定對象的「類型對象指針」成員中的地址。換言之,gettype方法返回的是指向對象類型的一個指針。這樣一來就能夠判斷出系統中任何對象的真實類型。