这是一个老问题(已经选择了答案),但我不相信这个问题真的得到了很好的回答。
首先,一点背景...
.NET 是如何工作的?
传统的 Windows .EXE 文件是一个二进制文件,它表示您的计算机可以理解的一系列机器语言指令,并调用 Win32 API,这些 API 是 Windows 的一部分,提供应用程序可以利用的服务。使用的机器语言非常适合您的计算机类型,Win32 调用使可执行文件非常依赖于 Windows。.NET 可执行文件不是那样的。
重要的是要认识到 .NET 可执行文件(.EXE 文件)实际上不是本机 Windows 应用程序。Windows 本身不了解如何在 .NET 可执行文件中运行代码。你的电脑也不理解。
与 Java 非常相似,.NET 应用程序由一种称为 CIL(通用中间语言)的语言的指令组成,您可以将其视为实际不存在的理想计算机的机器语言。在 .NET 中,这种理想化机器的软件实现称为公共语言运行时 (CLR)。Java 世界中的等价物称为 Java 虚拟机 (JVM)。在 Java 中,CIL 的等价物称为 Java 字节码。CIL 有时称为 MSIL(微软中间语言)。
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 会在您运行可执行文件时即时编译此 CIL 代码(JIT)——就在代码实际运行之前。这意味着 CIL 字节码被转换(编译)为在您的计算机上本地运行的实际机器代码。CLR 的这一部分称为 JIT 编译器,或者通常只是 JIT。
迄今为止,微软已经发布了四个版本的 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 应用程序可以调用的大量功能和服务。这些程序集中的绝大多数都是在 CLR 上运行的纯 CIL 代码。在 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 可以在这些系统上执行 .NET 代码,比在 Windows 上执行的 .NET 的 MS 版本要好得多。在实践中,MS 实现通常更优越(尽管并非在所有情况下)。
除了 CLR,Mono 还提供了构成 .NET 框架的大部分其他库(程序集)。就像 Microsoft 版本的 .NET(实际上更是如此)一样,Mono 程序集作为 CIL 字节码提供。这使得从 Mono 获取 *.dll 或 *.exe 文件并在 Windows、Mac 或 Linux 上未经修改地运行它成为可能,因为 CIL 是这些系统上 CLR 实现的“本机”语言。
就像在 Windows 上一样,Mono 支持多个版本的 CLR 和相关的程序集:
Mono 的早期版本(1.2 之前?)仅支持 CLR 1.0 或 1.1。Mono 直到它自己的 2.0 版本才支持 2.0 框架的大部分内容。
直到 2.4 版的 Mono 版本同时支持 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 程序来模拟 Linux 上 Win32 API 的 GDI+ 部分。这个库被称为“libgdiplus”。如果您从源代码编译 Mono,您会注意到您需要先构建这个“libgdiplus”文件,然后才能构建“mono”。在 Windows 上不需要“libgdiplus”,因为 Win32 API 的 GDI+ 部分已经是 Windows 的一部分。将 Mono 完整移植到新平台还需要移植这个“libgdiplus”库。
在 .NET 库的设计过度受 Windows 设计影响且不适合 Mac 或 Linux 等系统的领域,Mono 团队编写了 .NET 框架的扩展。Mono 扩展也只是 CIL 字节码,通常在 .NET 上运行良好。
与 Windows 不同,Linux 通常不会检测 .NET 可执行文件并默认启动 CLR。用户通常必须通过键入“mono appname.exe”或类似内容直接运行 CLR。这里的“mono”是实现 CLR 的应用程序,“appname.exe”是包含要执行的 .NET 代码的 EXE 文件。
为了让用户更轻松,Mono 应用程序通常包装在一个启动 CLR 的 shell 脚本中。这隐藏了 CLR 就像在 Windows 中一样被使用的事实。当遇到使用 PE 文件格式的文件时,也可以告诉 Linux 启动 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 兼容性足够好,可以从 .NET 的实际 MS 版本中获取(在合法的情况下)大量实际 Microsoft 代码,如 ASP.NET MVC,并在 Mac 或 Linux 上运行。总的来说,Mono 团队在实现 CLR 和框架的其余部分(类库/程序集)方面做得很好。
ASP.NET
在 Windows 上,Internet 信息服务器 (IIS) 知道如何调用 CLR 以将 .NET 作为 Web 应用程序的一部分执行。在 Linux/Mac 上有一个 Apache 模块 (mod_mono),它提供与 Apache 网络服务器类似的功能。此应用程序是用 C 语言编写的,并且还必须移植到新的体系结构中。
移植单声道
这个讨论已经确定了 Mono 的一部分,这些部分构建为“本机”可执行文件,并且必须存在于您要在其上运行 .NET 应用程序的系统上。
- CLR(包括 JIT 编译器)——通常称为 Mono
- libgdiplus(适用于本身不支持 GDI+ API 的系统 [只有 Windows 支持])
- mod_mono(允许 Apache 为 .NET Web 应用程序调用 CLR)
这三个组件,加上类库,提供了一个 .NET 环境,对于您需要运行的 .NET 可执行文件来说,它看起来是“本机”的。
这就是 Mono 的工作原理。