这是一个旧问题(已经有了选定的答案),但我认为没有人真正回答好这个问题。
首先,一些背景知识。。。
.NET是如何工作的?
传统的Windows .EXE文件是一个二进制文件,表示一系列计算机语言指令,你的电脑能够理解并调用Win32 API,这是Windows提供的应用程序可利用的服务的一部分。使用的计算机语言非常特定于您的计算机类型和Win32调用使可执行文件非常依赖于Windows。.NET可执行文件不是这样的。
重要的是要意识到,.NET可执行文件(.EXE文件)实际上并不是原生的Windows应用程序。Windows本身不知道如何运行.NET可执行文件中的代码。你的电脑也不懂。
与Java类似,.NET应用程序由称为CIL(Common Intermediate Language)的指令组成,它是一种可以看作是不存在的理想计算机的机器语言。在.NET中,这个理想化机器的软件实现称为公共语言运行时(CLR)。在Java世界中,相当于CLR的是Java虚拟机(JVM)。在Java中,等效于CIL的被称为Java字节码。CIL有时被称为MSIL(Microsoft中间语言)。
CIL被设计为在CLR(一种理想化的机器)上运行,但除此之外,它是跨平台的,这意味着CIL不关心您使用的计算机种类或正在运行的操作系统。
就像你需要在要运行Java的每个平台上都有一个本地版本的Java JVM一样,你需要一个本地版本的CLR来运行.NET CIL可执行文件。CLR是一个原生的Windows应用程序,就像上面描述的传统Win32 EXE文件一样。CLR本身特定于设计它运行的Windows实施和计算机体系结构。
无论您从哪个.NET语言开始(C#,VisualBasic,F#,IronPython,IronRuby,Boo等),它们都会被编译成CIL字节码。您可以轻松地将CIL程序“反汇编”为一种面向对象的汇编语言形式,这对人类易于阅读。你可以直接用CIL编写程序,但很少有人这样做。
在Windows上,CLR在运行可执行文件之前就实时(JIT)编译了CIL代码。这意味着CIL字节码被转换(编译)为实际在您的计算机上运行的本机机器码。CLR的这一部分称为JIT编译器或通常称为JIT。
迄今为止,Microsoft已经发布了四个版本的CLR:1.0、1.1、2.0和4.0。如果您想运行针对该运行时的.NET可执行文件,则需要在计算机上安装正确版本的CLR。CLR 2.0支持.NET 2.0、3.0和3.5应用程序。对于其他版本的.NET,.NET版本与CLR版本映射清晰。除了JIT/CLR之外,.NET还提供了一系列库(程序集),它们组成了.NET框架的其他部分,并提供了许多功能和服务,供.NET应用程序调用。这些程序集中的绝大多数都是纯CIL代码,在CLR上运行。在Windows上,一些程序集也会调用Win32 API。安装.NET时,您将安装CLR、类库(框架)以及一堆开发工具。每个CLR版本通常需要完整的这些“框架”程序集集合。某些.NET版本(例如3.0和3.5)添加了额外的框架程序集,而没有更新CLR或与该CLR关联的现有程序集。
Windows中的.EXE文件以可移植可执行(PE)文件格式交付,其中包含描述可执行文件并将文件标识为.NET文件或本地Win32文件的头文件。当Windows尝试运行.NET文件时,它会看到这个头文件,并自动代表您调用CLR。这就是为什么.NET EXE文件似乎可以在Windows上本地运行的原因。
好的,那么Mono是如何工作的呢?
Mono在Linux、Mac和其他平台上实现了CLR。Mono运行时(CLR)是一个原生应用程序,主要使用C语言编写,并编译成针对所设计运行的计算机系统的机器语言代码。与Windows上的情况类似,Mono运行时是针对操作系统和使用的机器类型特定的。
就像在Windows上一样,Mono运行时(CLR)即时编译您的.NET可执行文件中的CIL字节码以生成计算机可以理解和执行的本地代码。这样,.NET文件对Linux与Windows一样“本地”。
要将Mono移植到新架构,需要移植JIT/CLR。这就像将任何原生应用程序移植到新平台一样。
.NET代码在Linux或Mac上运行得有多好,实际上只是CLR在这些系统上实现得有多好的问题。理论上,Mono CLR可以比MS版本的.NET在Windows上更好地执行.NET代码。但实际上,MS的实现通常更优秀(虽然并非所有情况都如此)。
除了CLR之外,Mono还提供了组成.NET框架的大部分其余库(程序集)。与Microsoft的.NET版本一样(事实上更甚),Mono程序集也是以CIL字节码形式提供的。这使得可以将从Mono获取的*.dll或*.exe文件作为CIL是这些系统的CLR实现的“本地”语言,在Windows、Mac或Linux上运行而无需修改。
就像在Windows上一样,Mono支持多个CLR版本和相关程序集:
早期版本的Mono(1.2之前?)只支持CLR 1.0或1.1。
直到其自己的2.0版本,Mono才支持2.0框架的大部分内容。
Mono版本直到2.4版本为止,都支持CLR 1.1和CLR 2.0应用程序。
从Mono 2.6开始,添加了CLR 4.0,但CLR 2.0仍然是默认值。
从Mono 2.8开始,CLR 4.0成为默认值,不再支持CLR 1.1。
Mono 2.10仍然将CLR 4.0作为默认值,并继续支持CLR 2.0。
就像真正的.NET一样,但情况要少得多,有一些Mono程序集调用本地库。为了使System.Drawing程序集在Mono上工作,Mono团队编写了一个Linux程序来模拟Win32 API的GDI+部分。这个库叫做“libgdiplus”。如果你从源代码编译Mono,你会注意到你需要在构建“mono”之前构建这个“libgdiplus”文件。在Windows上不需要“libgdiplus”,因为Win32 API的GDI+部分已经是Windows的一部分。将Mono完全移植到新平台需要将这个“libgdiplus”库进行移植。
在.NET库的设计过程中受到Windows设计的影响较大且不适合Mac或Linux系统的区域,Mono团队编写了.NET框架的扩展。Mono扩展也只是CIL字节码,在.NET上通常也能正常工作。
与Windows不同,Linux通常不会检测.NET可执行文件并默认启动CLR。用户通常必须直接运行CLR,例如输入“mono appname.exe”。这里,“mono”是实现CLR的应用程序,“appname.exe”是包含要执行的.NET代码的EXE文件。
为了方便用户,Mono应用程序通常会被包装在一个启动CLR的shell脚本中。这样隐藏了与Windows一样使用CLR的事实。还可以告诉Linux在遇到使用PE文件格式的文件时启动CLR。通常不这样做,因为PE文件格式也用于本地Win32 Windows可执行文件,当然CLR(Mono)不支持。
在我所知道的情况下,没有技术原因可以阻止Linux使用PE启动器,然后根据需要启动理解本机Windows代码的系统(如Wine)或CLR(Mono)。
来回转移
任何坚持“完全托管”的.NET代码,这意味着它不调用非.NET代码,应该在所有平台上都能在Mono上正常工作。我经常在Linux和Mac上使用从Windows编译的.NET程序集(我没有其中的代码)。
我还可以拿到我在Mono上编译的任何代码,并在Windows上运行.NET。例如,我可以向客户端提供我用Mono编译的代码,并不用担心他是32位还是64位Windows。当然,客户端需要安装正确版本的.NET(正确的CLR)。 CLR 2.0已经存在很长时间了,你可以肯定几乎所有Windows用户都已经安装了它。Mono编译器和其他代码也只是CIL可执行文件,因此如果您喜欢,它们可以在Windows上运行得很好。
Mono的兼容性足够好,以至于实际微软代码的大部分,如ASP.NET MVC,可以从实际的MS版.NET中获取(如果合法),并在Mac或Linux上运行。总的来说,Mono团队在实现CLR和框架的其余部分(类库/程序集)方面做得非常出色。
在Windows上,Internet Information Server(IIS)知道如何调用CLR来执行.NET作为Web应用程序的一部分。在Linux / Mac上,有一个Apache模块(mod_mono),提供类似的功能给Apache Web服务器。这个应用程序是用C编写的,必须也适配到新的架构上。
这次讨论确定了Mono的部分是作为“本机”可执行文件构建的,并且必须存在于要运行.NET应用程序的系统上。
以下三个组件,再加上类库,提供了一个.NET环境,看起来“本地”于您需要运行的.NET可执行文件。
- CLR(包括JIT编译器)-通常称为Mono
- libgdiplus(对于不支持GDI + API的系统[仅适用于Windows])
- mod_mono(允许Apache调用CLR以执行.NET Web应用程序)
这就是Mono的工作原理。