.NET平台系列7 .NET Core 体系结构详解

   .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
  • 《CoreCLR is now Open Source》https://devblogs.microsoft.com/dotnet/coreclr-is-now-open-source/
posted @ 2021-05-19 08:54  张传宁  阅读(3890)  评论(2编辑  收藏  举报
页脚 HTML 代码