.Net,Mono,C# 编译流程及一部分编译原理

发布于 2021-07-17  40 次阅读


背景

其实很早之前就奇怪过一个问题:

类似于JAVA,C#这种跨平台性优良的语言,

我可以理解他们的同样一份代码,在不同平台的相同内核的虚拟机上表现相同

但是我无法理解他们能够在这么多不同CPU指令集环境下,维持高效的内核开发进度

这两天看了一些C#相关的编译原理,理解了其中的大部分问题

正文

首先是梦回JAVA,

在学习基于虚拟机的语言的时候,大多会遇到一堆偏底层的名词,同时有的名词只会出现在特定语言的导论里面,姑且总结其中一些

  • CIL(Common Intermediate Language) 通用中间件语言,可以理解为虚拟机的虚拟机机器码,并非本机环境的实际CPU可读机器码;通用二字的是因为这项标准是由微软惠普英特尔等巨头起草的
  • CLI(Common Language Infrastructure)通用语言架构,包含CIL等组件的整个通用语言架构
  • Native code 原生码本机CPU直接可读的机器码
  • Rumtime 运行时 (名词),指的是并非由当前程序开发者直接经手编写的代码/指令,其中包括各种init函数,start函数,和虚拟机环境启动函数以及各种类库

然后是查找过程中发现的几篇英语文章,感觉很透彻,一看就懂,翻译(机翻)部分引用如下

机器码:这是最明确的一种。它是使用字节码指令的代码,你的处理器(做实际工作的物理金属片)能够理解并直接执行。所有其他代码必须在你的机器能够执行之前被翻译或转换为机器代码。

原生码:这个术语有时被用于指机器代码(见上文)的地方。然而,它有时也被用来指非托管代码(见下文)。
非管理代码和管理代码:非管理代码指的是用C或C++等编程语言编写的代码,它被直接编译成机器代码。它与托管代码形成对比,后者是用C#、VB.NET、Java或类似的语言编写的,并在虚拟环境(如.NET或JavaVM)中执行,后者有点像在软件中 "模拟 "一个处理器。主要区别在于,托管代码通过使用垃圾收集和保持对对象的引用不透明,为你 "管理 "资源(主要是内存分配)。非托管代码是那种需要你手动分配和取消分配内存的代码,有时会造成内存泄漏(当你忘记取消分配时),有时会造成分段故障(当你过早取消分配时)。无人管理通常也意味着没有对常见的错误进行运行时检查,如空指针解读或数组边界溢出。
严格来说,大多数动态类型的语言--如Perl、Python、PHP和Ruby--也是可管理代码。然而,它们通常不被这样描述,这表明托管代码实际上是真正大的、严肃的、商业编程环境(.NET和Java)的一个营销术语。
汇编代码:这个术语通常指的是人们在真正想写字节码时写的那种源代码。汇编器是一个将这种源代码变成真正的字节码的程序。它不是一个编译器,因为它是1对1的转换。然而,这个术语在使用哪种字节码方面是模糊的:它可能是受管理的或未受管理的。如果是非管理的,产生的字节码是机器码。如果它是受管理的,它的结果是虚拟环境(如.NET)在幕后使用的字节码。管理代码(如C#、Java)被编译成这种特殊的字节码语言,在.NET中被称为通用中间语言(CIL),在Java中被称为Java字节码。通常情况下,普通程序员很少需要访问这种代码或直接用这种语言编写,但当人们这样做时,他们通常将其称为汇编代码,因为他们使用汇编器将其变成字节代码。

运行时描述的是在你的程序运行时执行的软件/指令,特别是那些你没有明确写的指令,但对于你的代码的正常执行是必要的。
像C这样的低级语言的runtime非常小(如果有的话)。像Objective-C这样更复杂的语言,允许动态消息传递,有一个更广泛的runtime