48

我在带有 .NET 的 Visual Studio 中使用过 C#,并且在 openSUSE Linux 上使用过 Mono,但我并不真正了解它是如何工作的。

如果我在 .NET 上的 Windows 中编写应用程序,这与 Mono 有何关系?我不能在没有 Wine 的情况下在 Linux 上执行一个 Windows .exe 文件,所以它不能帮助我执行在 Windows 中开发的应用程序。

纯粹是为了在 Linux(和其他)上拥有一个等效的 .NET 库来简化跨平台开发吗?例如,如果我是一家企业,想接触 Linux 客户,但真的想使用 .NET,那么 Mono 应该是我的选择吗?还是我还缺少什么?

4

8 回答 8

125

这是一个老问题(已经选择了答案),但我不相信这个问题真的得到了很好的回答。

首先,一点背景...

.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 的工作原理。

于 2011-03-04T01:09:56.380 回答
22

一个 Windows EXE 包含多个“部分”。简而言之,.net 代码 (=MSIL) 只是 EXE 的一部分,并且在 EXE 中还有一个“真正的”本机 Windows 部件,用作 .net 框架的某种启动器,然后执行 MSIL。

Mono 将只获取 MSIL 并执行它,而忽略本机 Windows 启动器的内容。

同样,这是一个简化的概述。

编辑:我担心我对深层细节的理解不足以提供很多细节(我大致知道 PE Header 是什么,但不是真正的细节),但我发现这些链接很有帮助:

NET 程序集结构——第二部分

.NET Foundations - .NET 程序集结构

于 2008-10-19T19:36:45.437 回答
11

实际上,您可以在 Linux 上使用 Mono 运行 .NET .exe 文件。这不需要 Wine。事实上,Mono 将程序编译为 .exe 文件,该文件可以在 Linux 上与 Mono 一起运行,也可以作为 Windows 上的可执行文件运行。

于 2008-10-19T19:33:56.487 回答
3

Mono 是 Microsoft 的 .NET CLR(公共语言运行时)的开源实现。这是运行 .NET 程序的一部分,这些程序不是使用本机代码,而是使用 CIL(通用中间语言),一种语言和机器中立的中间语言。运行时获取该中间代码并将其转换为机器代码。

在 Mono 的当前状态下,您可以获取使用 .NET 主要部分 (mscorlib.dll) 的 .NET 程序,并在 Mono 运行的任何地方运行它们,而不仅仅是 Windows。

于 2008-10-19T19:41:16.223 回答
2

但是正如上面提到的,Mono 是开源的,你不能仅仅相信它将是完整的 .NET 实现,它有一些不起作用的控件,你还必须小心你的应用程序将使用的 P/Invokes ,例如,您的应用程序将在 win32 下与 MCI(多媒体控制器接口)通信。但是我也在使用 mono 编写 GTK# 应用程序,但我也使用了我的 Windows 应用程序,这些应用程序无需任何重新编译就可以运行,正如我们上面的程序员同事提到的那样,也就是说,mono 是 Microsoft .NET 的开源替代品,默认情况下如果您正在构建 WinForms 或 Gtk# 应用程序,mono 将编译并为每个文件创建一个 .exe 程序集,当然,如果您愿意,它将创建一个动态链接库 (DLL),几乎就像在 .NET 中所做的那样。只是为了建议尝试编写一些 Gtk#(使用 MonoDevelop IDE,它有它的内置 gui 设计器,称为 stetic)。当然,mono 可以很好地替代 Web 服务,您可以在 .NET 上创建它们,也可以在 Apache 上托管它们(因为现在 Linux 托管比 Windows 便宜)Web 服务和其他 asp.net 应用程序将在具有 mod_mono 模块的 apache,该模块必须包含在 apache 中。

有点跑题了,但我只是想从我的经验中告诉你一个先睹为快的情况。

于 2008-10-19T19:49:25.300 回答
1

您也可以看看 MoMA(如果您的目标是将应用程序从 Win 移植到 Lin)。

Mono Migration Analyzer (MoMA) 工具可帮助您识别在将 .Net 应用程序移植到 Mono 时可能遇到的问题。它有助于查明特定于平台的调用 (P/Invoke) 和 Mono 项目尚不支持的区域。

现代艺术博物馆

这是一个比较 Mono 和 .NET Framework 3.5 已经实现的 BCL 类型的 webapp

http://mono.ximian.com/class-status/2.0-vs-3.5/index.html

于 2008-10-19T23:20:19.463 回答
0

为了进一步 Michael 的回应,我相信您必须在 Mono 上重新编译才能让应用程序在 Mono 运行时上运行。可能存在例外。我只玩过一点 Mono,而且我总是重新编译源代码。我从未尝试直接在 Mono 上运行 .NET 应用程序。

此外,Mono 1.9 应该完全兼容 .NET 2.0。Mono Olive 和 Moonlight 应该添加 .NET 3.0(少 WPF)和 Silverlight 功能。

于 2008-10-19T21:06:24.070 回答
0

它可能会对您有所帮助,Mono 的 C# 编译器是如何工作的?以及了解 Mono C# 编译器一书。

于 2012-02-17T14:35:58.930 回答