【CLR via C#】CSC將源代碼編譯成托管模塊

来源:http://www.cnblogs.com/PatrickLiu/archive/2016/12/06/6136908.html
-Advertisement-
Play Games

下圖展示了編譯源代碼文件的過程。如圖所示,可用支持 CLR 的任何一種語言創建源代碼文件。然後,用一個對應的編譯器檢查語法和分析源代碼。無論選用哪一個編譯器,結果都是一個托管模塊(managedmodule)。托管模塊是一個標準的 32 位 Microsoft Windows 可移植執行體(PE32 ...


      下圖展示了編譯源代碼文件的過程。如圖所示,可用支持 CLR 的任何一種語言創建源代碼文件。然後,用一個對應的編譯器檢查語法和分析源代碼。無論選用哪一個編譯器,結果都是一個托管模塊(managedmodule)。托管模塊是一個標準的 32 位 Microsoft Windows 可移植執行體(PE32)文件 6 ,或者是一個標準的 64 位Windows 可移植執行體(PE32+)文件,它們都需要 CLR 才能執行。順便說一句,托管的程式集總是利用了 Windows 的數據執行保護(Data Execution Prevention,DEP)和地址空間佈局隨機化(Address SpaceLayout Randomization,ASLR);這兩個功能旨在增強整個系統的安全性。


托管模塊的組成部分

   PE32 或 PE32+頭:標準 Windows PE 文件頭,類似於“公共對象文件格式(Common Object File Format,COFF)”頭。如果這個頭使用 PE32 格式, 文件能在Windows的 32 位或 64 位版本上運行。如果這個頭使用 PE32+格式,文件只能在 Windows 的 64 位版本上運行。這個頭還標識了文件類型,包括 GUI,CUI 或者 DLL,並包含一個時間標記來指出文件的生成時間。對於只包含 IL 代碼的模塊,PE32(+)頭的大多數信息會被忽視。對於包含本地 CPU代碼的模塊,這個頭包含了與本地 CPU 代碼有關的信息

  CLR 頭:包含使這個模塊成為一個托管模塊的信息(可由 CLR 和一些實用程式進行解釋)。頭中包含了需要的 CLR 版本,一些 flag,托管模塊入口方法(Main 方法)的 MethodDef 元數據 token,以及模塊的元數據、資源、強名稱、一些 flag 以及其他不太重要的數據項的位置/大小

  元數據:每個托管模塊都包含元數據表。主要有兩種類型的表:一種類型的表描述源代碼中定義的類型和成員,另一種類型的表描述源代碼引用的類型和成員

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

      本地代碼編譯器(native code compilers)生成的是面向特定 CPU 架構(比如 x86,x64 或 IA64)的代碼。相反,每個面向 CLR 的編譯器生成的都是 IL(中間語言)代碼。IL 代碼有時稱為托管代碼,因為 CLR 要管理它的執行。

    除了生成 IL,面向 CLR 的每個編譯器還要在每個托管模塊中生成完整的元數據。簡單地說,元數據(metadata)是一組數據表。其中一些數據表描述了模塊中定義的內容,比如類型及其成員。還有一些元數據表描述了托管模塊引用的內容,比如導入的類型及其成員。元數據是一些老技術的超集。這些老技術包括 COM 的“類型庫(Type Library)”和“介面定義語言(Interface Definition Language,IDL)”文件。要註意的是,CLR 元數據遠比它們完整。另外,和類型庫及 IDL 不同,元數據總是與包含 IL 代碼的文件關聯。事實上,元數據總是嵌入和代碼相同的 EXE/DLL 文件中,這使兩者密不可分。由於編譯器同時生成元數據和代碼,把它們綁定一起,並嵌入最終生成的托管模塊,所以元數據和它描述的 IL 代碼永遠不會失去同步。元數據有多種用途,下麵僅列舉一部分。

  *  編譯時,元數據消除了對本地 C/C++頭和庫文件的需求,因為在負責實現類型/成員的 IL 代碼文件中,已包含和引用的類型/成員有關的全部信息。編譯器可直接從托管模塊讀取元數據。

  *  Microsoft Visual Studio 使用元數據幫助你寫代碼。它的“智能感知(IntelliSense)”技術可以解析元數據,指出一個類型提供了哪些方法、屬性、事件和欄位。如果是一個方法,還能指出方法需要什麼參數。

  *  CLR 的代碼驗證過程使用元數據確保代碼只執行“類型安全”的操作。(稍後就會講到驗證。)。

  * 元數據允許將一個對象的欄位序列化到一個記憶體塊中,將其發送給另一臺機器,然後反序列化,在遠程機器上重建對象的狀態。

  *  元數據允許垃圾回收器跟蹤對象的生存期。垃圾回收器能判斷任何對象的類型,並從元數據知道那個對象中的哪些欄位引用了其他對象

將托管模塊合併成程式集

  CLR 實際不和模塊一起工作。相反,它是和程式集一起工作的。程式集(assembly)是一個抽象的概念,初學者往往很難把握它的精髓。首先,程式集是一個或多個模塊/資源文件的邏輯性分組。其次,程式集是重用、安全性以及版本控制的最小單元。取決於你對於編譯器或工具的選擇,既可以生成單文件程式集,
也可以生成多文件程式集。在 CLR 的世界中,程式集相當於一個“組件”。

  下圖有助於理解程式集。在這幅圖中,一些托管模塊和資源(或數據)文件準備交由一個工具處理。該工具生成單獨一個 PE32(+)文件來表示文件的邏輯性分組。實際發生的事情是,這個 PE32(+)文件包含一個名為“清單”(manifest)的數據塊。清單是由元數據表構成的另一種集合。這些表描述了構成程式集的文件,由程式集中的文件實現的公開導出的類型 7 ,以及與程式集關聯在一起的資源或數據文件。

預設是由編譯器將生成的托管模塊轉換成程式集。換言之,C#編譯器生成含有清單的一個托管模塊。清單指出程式集只由一個文件構成。

載入公共語言運行時

  你生成的每個程式集既可以是一個可執行應用程式,也可以是一個 DLL(其中含有一組由可執行程式使用的類型)。當然,最終是由 CLR 管理這些程式集中的代碼的執行。這意味著必須在目標機器上安裝好.NETFramework。

  C#編譯器生成的程式集要麼包含一個 PE32 頭,要麼包含一個 PE32+頭。除此之外,編譯器還會在頭中指定要求什麼 CPU 架構(如果使用預設值 anycpu,則不明確指定)。Microsoft發佈了 SDK 命令行實用程式 DumpBin.exe 和 CorFlags.exe,可用它們檢查編譯器生成的托管模塊所嵌入的信息。

執行程式集的代碼

    為了執行一個方法,首先必須把它的 IL 轉換成本地 CPU 指令。這是 CLR 的 JIT (just-in-time 或者“即時”)編譯器的職責。下圖展示了一個方法首次調用時發生的事情。

     就在 Main 方法執行之前,CLR 會檢測出 Main 的代碼引用的所有類型。這導致 CLR 分配一個內部數據結構,它用於管理對所引用的類型的訪問。在圖中,Main 方法引用了一個 Console 類型,這導致 CLR分配一個內部結構。在這個內部數據結構中,Console 類型定義的每個方法都有一個對應的記錄項 10 。每個記錄項都容納了一個地址,根據此地址即可找到方法的實現。對這個結構進行初始化時,CLR 將每個記錄項都設置成(指向)包含在 CLR 內部的一個未文檔化的函數。我將這個函數稱為 JITCompiler。

  JITCompiler 函數被調用時,它知道要調用的是哪個方法,以及具體是什麼類型定義了該方法。然後,JITCompiler 會在定義(該類型的)程式集的元數據中查找被調用的方法的 IL。接著,JITCompiler 驗證 IL 代碼,並將 IL 代碼編譯成本地 CPU 指令。本地 CPU 指令被保存到一個動態分配的記憶體塊中。然後,JITCompiler返回 CLR 為類型創建的內部數據結構,找到與被調用的方法對應的那一條記錄,修改最初對 JITCompiler 的引用,讓它現在指向記憶體塊(其中包含了剛纔編譯好的本地 CPU 指令)的地址。最後,JITCompiler 函數跳轉到記憶體塊中的代碼。這些代碼正是 WriteLine 方法(獲取單個 String 參數的那個版本)的具體實現。這些代碼執行完畢並返回時,會返回至 Main 中的代碼,並跟往常一樣繼續執行。現在,Main 要第二次調用 WriteLine。這一次,由於已對 WriteLine 的代碼進行了驗證和編譯,所以會
直接執行記憶體塊中的代碼,完全跳過 JITCompiler 函數。WriteLine 方法執行完畢之後,會再次返回 Main。
    下圖展示了第二次調用 WriteLine 時發生的事情。

 

  一個方法只有在首次調用時才會造成一些性能損失。以後對該方法的所有調用都以本地代碼的形式全速運行,無需重新驗證 IL 並把它編譯成本地代碼。JIT 編譯器將本地 CPU 指令存儲到動態記憶體中。一旦應用程式終止,編譯好的代碼也會被丟棄。所以,如果將來再次運行應用程式,或者同時啟動應用程式的兩個實例(使用兩個不同的操作系統進程),JIT 編譯器必須再次將 IL 編譯成本地指令。對於大多數應用程式,因 JIT 編譯造成的性能損失並不顯著。大多數應用程式都會反覆調用相同的方法。
在應用程式運行期間,這些方法只會對性能造成一次性的影響。另外,在方法內部花費的時間很有可能比花在調用方法上的時間多得多。還要註意的是,CLR 的 JIT 編譯器會對本地代碼進行優化,這類似於非托管 C++編譯器的後端所做的工作。同樣地,可能要花費較多的時間來生成優化的代碼。但是,和沒有優化時相比,代碼在優化之後將獲得更出色的性能。

       有兩個 C#編譯器開關會影響代碼的優化:/optimize 和/debug。下麵總結了這些開關對 C#編譯器生成
的 IL 代碼的質量的影響,以及對 JIT 編譯器生成的本地代碼的質量的影響。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ...
  • 1.開發人員在火狐瀏覽器里經常使用的工具有Firebug,httprequester,restclient......火狐瀏覽器有一些強大的插件供開發人員使用!需要的可以在附加組件中擴展。 2.httprequester,也是可以在附加組件中獲得的,你只要輸入這個名詞,搜索安裝。 web開發人員一般 ...
  • Ext.NET 4.1 最新版本破解 今天在將Ext.NET 4.1版本的程式發佈到公網時居然要license(localhost和127.0.0.1不收費),而且一年$4999,突然間覺得這是什麼鬼,居然還收費!如圖: 大大的一個UNLICENSED! 網上搜索破解方法,好像都沒什麼用,唯一有啟發 ...
  • 1、重覆輸入一個數,判斷該數是否是質數,輸入q結束?質數的判斷用方法來實現bool IsPrime(int number) 1 static void Main(string[] args) 2 { 3 // 要求:重覆讓用戶輸入一個數,判斷該數是否是質數,輸入q結束? 質數的判斷用方法來實現boo ...
  • params參數練習 1 namespace Test 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 //params 構造函數聲明數組,可變數組長度 8 UseParam1(4,2,3); 9 UserParam2( ...
  • petapoco是個基於T4模板的輕量級ORM,好用效率高,具體介紹略了 獲取註釋基本原理是調用資料庫::fn_listextendedproperty函數,獲取擴展屬性MS_Description technet參考資料:sys.fn_listextendedproperty (Transact- ...
  • 最近比較忙,前期忙公司手機端介面項目,各種開發+調試+發佈現在幾乎上線無問題了;雖然公司項目忙不過在期間抽空做了兩件個人覺得有意義的事情,一者使用aspnetcore開發了個人線上項目(要說線上其實只能ip訪問,沒有功能變數名稱哈哈),其架構組成由:aspnetcore1.0.0+redis+ postgr ...
  • 在項目開發中,除了對數據的展示更多的就是對文件的相關操作,例如文件的創建和刪除,以及文件的壓縮和解壓。文件壓縮的好處有很多,主要就是在文件傳輸的方面,文件壓縮的好處就不需要贅述,因為無論是開發者,還是使用者對於文件壓縮的好處都是深有體會。至於文件壓縮的原理,在我的另一篇博客中有簡單的介紹,在這裡就不 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...