.NET平臺系列7 .NET Core 體系結構詳解

張傳寧發表於2021-05-19
   .NET Core 是基於.NET Framework 為基礎,借鑑了其優秀的思想與強大的功能,經過重新設計與構建,實現了.NET Framework 中的部分功能(不包含Windows UI部分),比如JIT、垃圾收集器(GC)、CLR、BCL等。
  • 執行時

  .NET Core 中包含2種執行時:CoreCLR 與 Native RunTime。CoreCLR 是一個開源的JIT執行時,它將程式碼編譯成中間語言(IL),在終端機器執行時再轉換成機器碼。NativeRuntime 將 C# 、F#或VB程式碼直接轉換為原生機器碼直接執行。

  • BCL

   BCL 即 Base Classlibrary 基礎類,例如 File、System、Console、XML、ADO.NET、日期時間等類庫。

  • App Model
  提供上層應用產品開發,Web應用、遊戲開發、桌面程式、物聯網整合應用等。
 
.NET Core 核心組成

  .NET Core 是由許多專案所組成,除了基本的類庫(Core FX) 之外,也包含採用 RyuJIT 編譯的執行平臺 Core CLR、編譯器平臺.NET Compiler Platform、採用 AOT 編譯技術執行最優化的包 Core RT (.NET Core Runtime),以及跨平臺的 MSIL 編譯器 LLILC (LLVM-based MSIL Compiler) 等專案。

【底層】編譯器

  • Roslyn  

  .NET Compiler Platform (專案程式碼為 Roslyn) s是一個開源的跨平臺原始碼編譯器。它是將 .NET平臺的編譯架構標準化的平臺,它可提供程式管理工具 (如整合開發環境) 相當多的情報,用以發展有助於編寫程式與管理程式結構所需要的功能,例如型別資訊、語法結構、參考連結、語義、編譯器、自動化、錯誤回報等等功能,只要是遵循 CLI 標準的程式語言,都可以利用 .NET Compiler Platform 實現出編譯器,讓程式管理工具能實現如語法提示、語法自動完成、關鍵字高亮等視覺化能力。

  .NET Compiler Platform 可同時支援 .NET Framework 4.6 以上版本,.NET Core 也原生支援。

  • RyuJIT

  RyuJIT 是微軟發展的新式即時編譯器 (Just-in-Time Compiler),用以替換現有的 .NET Framework 的 JIT 以及 JIT64 即地編譯器,依據微軟公佈的測試報告,RyuJIT 的效能較前一代的 JIT 提升約 25%,並支援SIMD(Single Instruction, Multiple Data) 的技術。RyuJIT 同時應用於 .NET Framework 4.6 以及 .NET Core  。

  最主要的是它實現了AMD64的架構。

  • Core CLR

  Core CLR 移植 .NET Framework 的 CLR 的功能,包含核心程式庫 mscorlib、JIT 編譯器、垃圾收集器 (GC) 以及其他執行 MSIL 所需要的執行期環境。核心功能包括:記憶體管理、程式集載入、安全性、異常、執行緒管理等。

  • Core RT

  Core RT 是以AOT (Ahead-of-time) 編譯方式為主的核心功能,在 .NET Core 內稱為 Core RT,在 UWP 則是稱為 .NET Native。
  Core RT 會在建造時期 (非執行期) 在編譯時將 MSIL 轉換成平臺本地的機器碼,以獲取較短的引導時間 (JIT 採用的是執行時期編譯,使得引導時間拉長),以及記憶體用量減少的優點。Core RT 會在不同的平臺使用不同的 AOT 技術:

         · Windows 上使用的是 .NET Native。
         · macOS 與 Linux 上使用的是 LLILC (同時支援 JIT 和 AOT)。

  CoreRT 提供了一套AOT 的機制,可以將.NET Core程式編譯成原生程式碼,不依賴 .NET 執行時而執行在宿主機器上。
除此之外兩個執行時大部分功能程式碼是共享的,比如GC。AOT的優化帶來不少好處:

  • 編譯後生成一個單檔案,包含所有的依賴,包括 CoreRT,無需安裝Framework或.NET Core。
  • 啟動時是機器碼,不需要生成機器碼,也不要載入JIT編譯器。
  • 可以使用其他優化編譯器,包括 LLILC ,IL to CPP。

  CoreRT有兩個方式生成機器碼,第一個使用是直接編譯IL成機器碼。預設情況下,RyuJIT 作為一個 AOT 編譯器將IL編譯成機器碼。另一個方式是將C#程式碼編譯成C++程式碼,然後呼叫對應平臺的C++編譯器優化編譯成機器碼。

  • LLILC

  LLILC (LLVM-based MSILCompiler,英文發音為 "lilac") 是 .NET Core 在非 Windows 平臺的 MSIL 編譯器 ,基於 ECMA-335 (Common Language Infrastructure) 的標準將 MSIL 編譯成原生碼執行,適用於可執行 LLVM 的作業系統,例如 macOS與 Linux 作業系統。LLILC 同時支援 JIT (內含 RyuJIT 的實現) 以及 AOT (未來將開始支援) 的編譯方式。

【中間層】核心類庫

  • CoreFX(.NET Core Libraries)

  CoreFX主要包含數個公共庫,例如 System.Collections, System.IO, System.Xml等。CoreFX是 .NET Standard Library 的實現,同樣的.NET Framework 4.6.3也是基於.NET Standard Library的實現。它們目前都是基於.NET Standard Library1.6版本,具體見下表:

 

  開源地址:https://github.com/dotnet/corefxlab

【應用層】開發框架

   開發框架是開發基於UI應用的框架集,包括了ASP.NET Core(用於建立Web App),和 UWP(用於建立Windows10 App) 等。

Roslyn 編譯原理

Roslyn編譯器用於將 C#、F#或VB.NET 程式碼編譯為程式集(Assembly),它的編譯過程是一個管道式的處理過程,一共包含4個步驟,具體過程見下圖。

 

(1)Parser(解析)
  根據語法對原始碼進行解析。

(2) Declaration(宣告)

  為程式碼生成後設資料(metadata),後設資料是一個資料表的集合,描述了在當前程式碼中定義的資料型別和成員,同時也描述了引用的型別及成員。

(3)Bind(繫結)
  將生成的IL程式碼與描述它的後設資料繫結在一起,生成託管模組(managed module)。

(4)Emit(生成)
  將一個或多個託管模組合併生成程式集(assembly)。

.NET Core 程式碼開發、部署、執行過程

從上圖可以看到使用JIT編譯和使用AOT編譯原始碼並執行程式是兩種不同的流程。

如果使用JIT編譯器部署程式時只需要將程式打包為IL的assemblies,在方法第一次執行前編譯器將IL編譯為目標機機器碼(Native code),而AOT編譯會在編譯時將原始碼直接編譯為目標機機器碼。

AOT將原始碼編譯為機器碼,擁有如下特性:

(1)用靜態程式碼替換反射,例如如果一個值型別(value type)沒有重寫 ValueType.Equals 的equals()方法,預設情況判斷相等,會使用反射找到FiledInfo以確定Type是否相等,然後再比較Value是否相等。而在AOT編譯中由於替換了反射因此只能比較Value是否相等。

(2)依賴的第三方類庫以及.NET Libraries均打包至最終編譯的程式中。

(3)打包後的程式執行在一個精簡版的執行時上(CoreRT)主要包含垃圾回收器,而執行時也會打包在app檔案中。

(4)雖然編譯時會替換反射程式碼,但遇動態反射程式碼無能為力,執行時若遇動態反射呼叫則會因找不到對應的後設資料及實現而丟擲異常。解決辦法是編譯前配置執行時指令檔案(Runtime directive file)指定需要用到的程式集。

 參考:https://docs.microsoft.com/zh-cn/dotnet/standard/components

       https://docs.microsoft.com/zh-cn/dotnet/standard/clr

            https://www.cnblogs.com/xuanhai/p/6691647.html

相關文章