认识 Mono
如果您曾经为 Linux 桌面编写过应用程序,甚至研究过编写应用程序,您就会熟悉各种 GUI 工具包可用的多种语言绑定。这是为 Linux 编写 GUI 应用程序的优势之一;您不会被锁定在特定的编程语言中。不幸的是,您很快就会意识到不同的语言绑定提供的 API 完整性各不相同。当使用不同的语言时,您从一种语言中使用的 widget 可能尚不支持。这是支持多种语言的缺点。维护 API 所需的工作量随着每组绑定的增加而增加。对原始 API 的更改或更新必须在每个语言包装器中复制。
现在想象一下,一个单一的 GUI 工具包,可以从任何编程语言访问,而无需依赖 API 包装器——一个为每种使用它的语言提供相同功能的工具包。Mono 有潜力提供这一点,以及更多,通过提供编程语言的独立性以及编程语言的交互性。
Mono 的生命大约在两年前始于 Linux 软件公司 Ximian, Inc.。Ximian 以其 Ximian 桌面、Evolution PIM/电子邮件客户端、Red Carpet 升级系统和充满热情的 CTO Miguel de Icaza 而闻名。Miguel de Icaza 认识到几个新提出的标准的潜力,开始原型设计后来成为 Mono 项目的东西。
那么,是什么标准吸引了 Miguel de Icaza 的眼球?它们是 ECMA-334 和 ECMA-335,即微软 .NET 开发平台中核心技术的规范,这已经不是什么秘密了。在这一点上,可能重要的是指出 .NET 开发平台和统称“.NET”之间存在差异。微软用广泛的 .NET 术语涵盖了包括操作系统、开发工具、网络服务和应用程序在内的整套产品和服务。我们只关注 .NET 的一部分。
2000 年 10 月,微软、惠普和英特尔联合提交了一个名为通用语言基础设施 (CLI) 的运行时环境和一个新开发的名为 C# 的面向对象语言的规范。到 2001 年下半年,Ximian 正式启动了 Mono 项目,以基于拟议的标准提供 .NET 开发平台的开源实现。2001 年 12 月,欧洲计算机制造商协会 (ECMA) 正式批准 CLI 和 C# 语言的规范为标准。
CLI 规定了一个基类库和一个运行时环境,该环境提供诸如即时 (JIT) 编译、内存管理、异常处理、加载和链接以及安全管理等服务。为了更好地说明这一点,将它与编译源代码的传统方法进行比较会有所帮助。
传统上,源代码由编译器转换为特定于机器的指令。然后,这些指令直接在处理器上执行。为 x86 处理器系列编译的程序在 PPC 处理器上将无法执行,除非首先为该处理器重新编译。这使得软件难以针对多个硬件平台,因为必须为每个平台保留不同版本的编译代码。
作为替代方案,为运行时环境编译的源代码被转换为一组不依赖于底层硬件的中间指令。然后,中间指令可以通过几种不同的方式执行。一种方法是使用解释器。解释器加载中间指令,然后执行它们,实际上充当虚拟机。在第二种方法中,中间形式在运行时或安装时 JIT 编译为特定于机器的指令,然后直接执行。由于 JIT 编译执行本机平台指令,因此可以针对目标处理器优化编译。JIT 编译器可以通过仅将正在使用的部分转换为本机指令,然后将这些指令存储在内存中以供后续调用,从而进一步提高执行速度。
使用运行时环境的平台独立性的权衡在于执行速度。与编译为本机指令的传统方法相比,运行时速度较慢。慢多少取决于具体情况以及运行时使用哪种执行方法。但总的来说,解释器提供最慢的执行速度。JIT 编译器的性能更接近传统编译的性能,因为两者都生成本机指令。运行时本身的开销仍然使速度性能略微落后。
我知道你在想什么。面向对象的语言、基类库、运行时环境——这听起来很像 Java。嗯,你是对的。CLI 的组件与 Java 中发现的组件非常相似。但是,有一个根本的区别。Java 运行时仅为 Java 语言设计。虽然确实有一些其他语言已被移植以输出 Java 字节码并在 JVM 上运行,但这仍然不足以满足 CLI 支持的语言中立性。从一开始,CLI 就被设计为多种编程语言的执行环境。CLI 的数据类型系统可以支持命令式语言(如 C 或 Pascal)以及面向对象的语言。CLI 不仅具有执行多种语言的功能(语言独立性),而且还提供了框架以允许这些语言彼此共享数据(语言交互),包括跨语言异常处理。在一个语言中创建的对象可以在另一个语言中继承。通过检查其核心组件,可以找到 CLI 如何实现这种语言中立性级别的详细信息。
CLI 的核心是通用类型系统 (CTS)。CTS 定义了一个共享数据类型系统以及在声明、使用和管理类型时使用的规则。通过采用严格执行的类型系统,CLI 可以确保维护类型安全,并使语言能够与另一种语言的类型互操作。为了适应多种不同的编程语言,CTS 提供了两种主要的数据类型,其中包含多个子类型、值(值类型)和对象(引用类型)。值保留用于表示简单数据类型,例如整数和浮点值。对象用于编程语言所需的更复杂的实体。
通用语言规范 (CLS) 概述了编译器在为跨语言交互生成库和二进制文件时必须遵守的框架。CLS 实际上是 CTS 的一个子集,提供了一个合理的类型系统和语言编译器必须支持的规则,以便生成可以被其他语言使用或扩展的编译代码。语言有能力选择支持多少 CLS。允许使用任何 CLS 类型的语言称为 CLS 使用者。允许创建或扩展 CLS 类型的语言称为 CLS 扩展器。完全接受 CLS 的语言既是使用者又是扩展器。
当源文件由符合 CLI 的编译器编译时,会输出一个名为可移植可执行文件 (PE) 的二进制文件,有时也称为程序集,尽管程序集可以由一个或多个文件组成。PE 包含两个重要的信息。第一个是元数据。元数据用于描述使用的类型以及 CLI 用于定位和加载类、布局内存和其他运行时信息的信息。第二个是通用中间语言 (CIL) 字节码。CIL 是一组与语言无关的中间指令。当为 CLI 编译语言时,会生成 CIL 字节码。CIL 足够健壮,可以处理各种不同的编程语言,并且旨在有效地转换为本机平台指令。清单 1 中可以看到用 C# 编写的“hello world”程序的 CIL 指令片段。
虚拟执行系统 (VES) 为执行为 CLI 编写的程序提供环境。它加载、链接、管理内存、处理安全性和异常,并为执行 CIL 指令提供支持框架。
CLI 提供的内存管理由垃圾回收器 (GC) 管理。与其他运行时环境不同,CLI 的 GC 可以在源代码中打开和关闭。由 GC 分配和销毁的数据称为托管数据。当 GC 未用于数据时,它被称为非托管数据。托管代码,由 CLI 执行的源代码,可以访问托管数据和非托管数据。
Mono 项目的目标是双重的。首先,它提供了 CLI 和 C# 的 ECMA 标准的实现。其次,它增加了与 Microsoft .NET 开发平台的兼容性。每个部分都有其内在的价值,并以不同的方式使 Linux 受益。例如,如果 .NET 兼容性不再可用,Mono 仍将是 Linux 的有价值的开发框架。
然而,通过将 .NET 兼容性添加到 Linux 平台,Mono 使为 Windows 开发的软件可用于 Linux。沿着同样的思路,希望过渡到 Linux 应用程序开发的开发人员将可以使用他们已经熟悉的开发框架,从而降低学习曲线障碍。
Mono 项目正在努力交付的 .NET 的主要部分是 Win Forms (System.Windows.Forms)、ADO.NET 和 ASP.NET。Win Forms 包含创建与 Microsoft Windows 兼容的 GUI 应用程序所需的所有方法、类和事件。由于几乎不可能用本机 Linux GUI 工具包模拟 Windows GUI API 调用,Mono 正在使用 WineLib (www.winehq.com) 来提供 Windows 界面。如果您曾经见过在 Wine 中运行的应用程序,您就会知道它看起来与 Linux 桌面环境截然不同。为了解决这个问题,Mono 正在考虑向 Wine 添加主题支持,以便为 widget 使用与桌面其余部分相同的渲染例程。
ADO.NET 包含 Mono 的 .NET 数据访问类。ADO.NET 提供的不仅仅是数据库访问。它提供了一种基于 XML 的断开连接、可扩展的方法来访问来自任何来源的数据的模型。在撰写本文时,大约有十几个数据库作为 Mono ADO.NET 数据提供程序工作。工作仍在继续,以提高成熟度并增加对其他数据库供应商的支持。
Mono 中的 ASP.NET 支持分为两个部分,Web 表单和 Web 服务。Web 表单为 Web 应用程序创建用户界面。与 Win Forms 非常相似,Web 表单为按钮、文本框或由多个简单控件组成的复杂控件提供属性、方法和事件。这允许使用类似于 GNOME 上的 Glade 的拖放技术在快速应用程序开发 (RAD) 环境中创建 Web 表单界面。这会将表示与逻辑分离,并减少所需的编码量。Web 服务提供基于 SOAP 的远程过程调用支持。使用无处不在的互联网协议(如 XML 和 HTTP),Web 服务允许通过网络甚至通过防火墙共享数据或逻辑。CLI 支持的任何语言都可以用于编程 ASP.NET 的逻辑。这也意味着 ASP.NET 代码是编译的,而不是像以前版本的 ASP 和其他 Web 脚本语言那样解释的。ASP.NET 可用于 Mono 中的 XSP Web 服务器或 Apache 2 的 mod_mono 组件。
除了实现 .NET 的 Mono 类库之外,还有几个其他库和工具提供了有趣且有用的功能
GTK#、Qt# 和 Wx.NET 为流行的 Linux GUI 工具包提供 C# 绑定。通过这些 C# 包装器,所有可以在 Mono 上运行的语言都可以访问相同的 GUI 工具包
OpenGL#、MonoGLo 和 CsGL 为流行的 2-D/3-D 图形 API OpenGL 提供绑定。
SDL.NET 为 SDL 游戏库提供绑定。
Gst# Gstreamer 多媒体框架绑定。
许多通信库,包括 .NET Jabber 和 Gnutella。
NAnt 构建工具(类似于 Ant)。
当然,这些只是一些示例,但它们足以说明使用 Mono 为 Linux 和其他平台进行开发的潜力。
测试 Mono 的第一步是访问项目网站 (www.go-mono.com) 并下载最新的源代码 tarball 或平台二进制文件。目前,Mono 仅移植到 Linux 和 Windows,但 Mac OS X、FreeBSD 和其他平台的工作正在进行中。二进制文件可用于各种 Linux 发行版,包括 Debian、Red Hat、SuSE 和 Mandrake。如果您使用 Ximian Red Carpet,这些文件也可以在 Mono 频道中找到。对于本文,我们使用的是 Mono 0.20 版本。您会注意到,除了提供运行时、C# 编译器和类库的 Mono 包之外,还有一些其他好东西可以玩,例如 Mono 调试器、XSP Web 服务器和 Monodoc 文档浏览器。
如果您在安装 Mono 时遇到问题,请查看网站上提供的教程。
Mono 当前与以下组件打包在一起
C# 和 Basic 语言编译器。
VES,由 JIT 编译器和相关的垃圾回收器、安全系统、类加载器、验证器和线程系统组成。还包括一个解释器。
一组用 C# 编写的类库,实现了 CLI 标准中定义的类、.NET FCL 的一部分类以及其他特定于 Mono 的类。
各种实用程序。
Mono C# 语言编译器是 mcs。在一个有趣的编程壮举中,mcs 是用 C# 编写的。自 Mono 0.10 以来,mcs 甚至能够编译自身。如果您对命令行选项的详细信息感兴趣,这些选项与 Microsoft C# 编译器提供的命令行选项兼容,则可以查阅完整的手册页。
Mono 等效于 Visual Basic.NET 的编译器 MonoBasic 是 mbas。虽然不如 C# 编译器那样成熟,但 mbas 提供了足够的功能来尝试一下 Basic。
Mono 附带了两个执行环境,mono 和 mint。mono 是与 CLI 的 VES 定义兼容的 JIT 编译器。另一方面,mint 是一个解释器。它被提供作为 mono 的一个易于移植的替代方案,mono 目前仅在 x86 平台上运行。为了获得最大的代码执行速度,请使用 mono。
Mono 还提供了一些有趣的实用程序,例如 monodis 和 pedump。monodis 用于反汇编编译后的程序集并输出相应的 CIL 代码。它用于显示清单 1 的示例 CIL 代码。如果您好奇想了解更多 CIL 的外观,或者想了解可移植可执行文件的组成,请试用这些。
现在我们已经熟悉了 Mono 的组件,是时候试用一下它们了。为了体验 Mono 的语言交互,我们用 C# 编写一个带有一个方法的简单类,并从 MonoBasic 程序中调用它。
清单 2 显示了 C# 库 ljlib.cs,清单 3 显示了 MonoBasic 程序 hello.vb。
第一步是将 ljlib.cs 编译成库。编译后的库具有 .dll 扩展名,编译后的可执行文件具有 .exe 扩展名。要编译成库,请在 mcs 中使用 -target:library 开关
[jdq@newton]$ mcs -target:library ljlib.cs Compilation succeeded
这将创建 ljlib.dll 文件,其中包含 LJlib 命名空间和 Output 类。现在我们需要编译 hello.vb 程序。为了使用我们刚刚创建的 ljlib.dll 文件,我们需要告诉 MonoBasic 编译器将其用作引用。我们使用 -r 开关来做到这一点
[jdq@newton]$ mbas -r ./ljlib.dll hello.vb Compilation succeededmbas 的输出是 PE hello.exe。它可以使用 mono 执行
[jdq@newton]$ mono hello.exe Hello Linux Journal!就这样,两种语言,C# 和 MonoBasic,在同一个运行时上执行并协同工作。这是一个简单的示例;但是,它确实演示了 CLI 的语言独立性和互操作性,并暗示了 Mono 作为开发平台的强大功能。

Julio David Quintana 是一名电子工程师,于 1997 年偶然接触到 Linux,从此便深深着迷。如果您发现语法或事实错误,则无法联系到他。但是,对于赞扬和赞美,jdq@jdqi.com 可以正常工作。