Getting to Know Mono
If you have ever written an application for the Linux desktop, or even looked into writing one, you are familiar with the multitude of language bindings available for the various GUI toolkits. This is one of the strengths of writing GUI applications for Linux; you are not locked in to a particular programming language. Unfortunately, you quickly come to realize that different language bindings offer varying amounts of API completeness. A widget you used from one language isn't yet supported when using a different language. This is the downside of supporting multiple languages. The amount of work needed to maintain an API increases with each set of bindings. A change or update to the original API must be replicated in each of the language wrappers.
Now imagine a single GUI toolkit, accessible from any programming language without having to rely on API wrappers—a toolkit that offers the same functionality to every language that uses it. Mono has the potential to provide this, plus much more, by offering programming language independence as well as programming language interaction.
Life for Mono began about two years ago at the Linux software company Ximian, Inc. Ximian is known for their Ximian Desktop, Evolution PIM/e-mail client, Red Carpet upgrade system and enthusiastic CTO Miguel de Icaza. Recognizing the potential in a couple of newly proposed standards, Miguel de Icaza began prototyping what would later become the Mono Project.
So what were these standards that caught Miguel de Icaza's eye? It's no secret that they were ECMA-334 and ECMA-335, the specifications for the core technologies in Microsoft's .NET development platform. At this point, it probably is important to point out that there is a difference between the .NET development platform and the blanket term “.NET”. Microsoft covers a whole slew of products and services, including operating systems, development tools, network services and applications, with the expansive .NET term. We are concerned with only a portion of .NET.
In October 2000, Microsoft, Hewlett-Packard and Intel jointly submitted the specifications for a runtime environment known as the Common Language Infrastructure (CLI) and a newly developed object-oriented language named C#. By the second half of 2001, Ximian officially had launched the Mono Project to provide an open-source implementation of the .NET development platform based on the proposed standards. In December 2001, the European Computer Manufacturing Association (ECMA) officially ratified as standards the specifications for the CLI and C# language.
The CLI lays out a base class library and a runtime environment that provides services such as Just In Time (JIT) compilation, memory management, exception handling, loading and linking and security management. To illustrate this better, it helps to compare it to the traditional method of compiling source code.
Traditionally, source code is converted by the compiler to machine-specific instructions. The instructions are then executed directly on the processor. A program compiled for the x86 processor line will fail to execute on a PPC processor without first being recompiled for that processor. This makes it difficult for software to target multiple hardware platforms, as a different version of compiled code must be kept for each one.
As an alternative, source compiled for a runtime environment is converted to an intermediate set of instructions that are not dependent on the underlying hardware. The intermediate instructions then can be executed in a couple of different ways. One method is to use a interpreter. The interpreter loads the intermediate instructions and then executes them, in essence acting as a virtual machine. In a second method, the intermediate form is JIT-compiled at runtime or installation time into machine-specific instructions and then executed directly. Because JIT compiling executes native platform instructions, compiling can be optimized for the target processor. The JIT compiler can increase execution speed further by converting only the portions that are being used into native instructions and then storing those in memory for subsequent calls.
The trade-off for having the platform independence of using a runtime environment is in execution speed. Compared to the traditional method of compiling to native instructions, the runtime is slower. How much slower depends on the specific situation and which method of execution the runtime uses. Generally though, an interpreter provides the slowest execution speed. The performance of a JIT compiler is much closer to the performance of traditional compiling because both produce native instructions. The overhead of the runtime itself still keeps the speed performance slightly behind.
I know what you are thinking. An object-oriented language, a base class library, a runtime environment—this sounds a lot like Java. Well, you are right. The components of the CLI are very similar to those found in Java. However, there is one fundamental difference. The Java runtime was designed only for the Java language. Although it is true that a handful of other languages have been ported to output Java bytecode and run on the JVM, this still falls short of the language neutrality supported by the CLI. From the ground up, the CLI was designed to be the execution environment for many programming languages. The data type system of the CLI can support imperative languages, like C or Pascal, as well as object-oriented languages. Not only does the CLI have facilities to execute multiple languages (language independence), it also provides the framework to allow those languages to share data with each other (language interaction), including cross-language exception handling. An object created in one language can be inherited in another. Details of how the CLI achieves this level of language neutrality can be found by examining its core components.
At the heart of the CLI is the Common Type System (CTS). The CTS defines a shared data type system and the rules used in declaring, using and managing the types. By employing a strictly enforced type system, the CLI can ensure that type safety is maintained as well as make it possible for languages to interoperate with types of another language. In order to accommodate a multitude of different programming languages, the CTS furnishes two main data types that contain multiple subtypes, values (value types) and objects (reference types). Values are reserved for representing simple data types such as integers and floating-point values. Objects are used for the more complex entities required by programming languages.
The Common Language Specification (CLS) outlines the framework compilers must adhere to when generating libraries and binaries for cross-language interaction. The CLS is actually a subset of the CTS, providing a reasonable type system and rules a language compiler must support in order to produce compiled code that can be used or extended by other languages. A language has the ability to choose how much of the CLS to support. Languages that allow any CLS type to be used are called CLS consumers. Languages that allow CLS types to be created or extended are called CLS extenders. A language that fully embraces the CLS is both a consumer and extender.
When a source file is compiled by a CLI-compliant compiler, a binary file called a portable executable (PE) or sometimes referred to as an assembly, though an assembly can consist of one or more files, is output. The PE contains two important pieces of information. The first is metadata. Metadata is used to describe the types used as well as information the CLI uses to locate and load classes, lay out memory and other execution-time information. The second piece is the Common Intermediate Language (CIL) bytecode. CIL is a language-independent set of intermediate instructions. When a language is compiled for the CLI, CIL bytecode is produced. CIL is robust enough to to handle a myriad of different programming languages and is designed to be converted efficiently to native platform instructions. A snippet of CIL instructions for a “hello world” program written in C# can be seen in Listing 1.
The Virtual Execution System (VES) provides the environment for executing programs written for the CLI. It loads, links, manages memory, handles security and exceptions and provides the support framework for executing CIL instructions.
The memory management supplied by the CLI is administered by a garbage collector (GC). Unlike other runtime environments, the GC of the CLI can be switched on and off within the source code. The data allocated and destroyed by the GC is called managed data. When the GC is not used on data, it is referred to as unmanaged data. Managed code, source code executed by the CLI, can access both managed and unmanaged data.
The goal of the Mono Project is twofold. First, it provides an implementation of the ECMA standards for the CLI and C#. Second, it adds compatibility with the Microsoft .NET development platform. Each part has its own intrinsic value and benefits Linux in different ways. If, for example, .NET compatibility was no longer available, Mono would remain a valuable development framework for Linux.
However, by adding .NET compatibility to the Linux platform, Mono makes software developed for Windows available to Linux. Along the same line of thought, developers wishing to make the transition to Linux application development will have access to a development framework with which they are already familiar, thereby lowering the learning-curve barrier.
The major portions of .NET that the Mono Project is working on delivering are Win Forms (System.Windows.Forms), ADO.NET and ASP.NET. Win Forms contain all the necessary methods, classes and events for creating GUI applications compatible with Microsoft Windows. Because it is nearly impossible to emulate the Windows GUI API calls with native Linux GUI toolkits, Mono is using WineLib (www.winehq.com) to provide the Windows interface. If you have ever seen an application running in Wine, you know it looks nothing like Linux desktop environments. To solve this, Mono is looking to add theming support to Wine to use the same rendering routines for the widgets as the rest of the desktop.
ADO.NET contains the .NET data access classes for Mono. ADO.NET offers more than mere database access. It provides a model for accessing data from any source in a disconnected, scalable method based on XML. At the time of this writing, about a dozen databases are working as Mono ADO.NET data providers. Work continues to increase the maturity and add additional database vendor support.
ASP.NET support in Mono is divided into two parts, web forms and web services. Web forms create the user interface for a web application. Much like Win Forms, web forms provide properties, methods and events for controls such as buttons, text boxes or complex controls made of multiple simple controls. This allows web form interfaces to be created in rapid application development (RAD) environments using drag-and-drop techniques similar to Glade on GNOME. This separates the presentation from the logic and lessens the amount of coding needed. Web services offer SOAP-based remote procedure call support. Using ubiquitous internet protocols like XML and HTTP, web services allow the sharing of data or logic over the network and even through firewalls. Any language supported by the CLI can be used to program the logic for ASP.NET. This also means that ASP.NET code is compiled and not interpreted like previous versions of ASP and other web-scripting languages. ASP.NET is available for Mono in either the XSP web server or in the mod_mono component for Apache 2.
In addition to the Mono class libraries implementing .NET, several other libraries and tools offer interesting and useful functionality:
GTK#, Qt# and Wx.NET provide C# bindings for the popular Linux GUI toolkits. With these C# wrappers, all languages that can run on Mono have access to the same GUI toolkits:
OpenGL#, MonoGLo and CsGL provide bindings for the popular 2-D/3-D graphics API OpenGL.
SDL.NET provides bindings for the SDL game library.
Gst# Gstreamer multimedia framework bindings.
Many communication libraries, including .NET Jabber and Gnutella.
NAnt build tool (similar to Ant).
Of course, these are only a few examples, but they're enough to illustrate the potential for developing with Mono for Linux and other platforms.
The first step in taking Mono out for a test spin is to visit the project web site (www.go-mono.com) and download the latest source tarballs or platform binaries. Currently, Mono has been ported only to Linux and Windows, but work is being done on Mac OS X, FreeBSD and other platforms. Binaries are available for a variety of Linux distributions including Debian, Red Hat, SuSE and Mandrake. If you use Ximian Red Carpet, the files also are available in the Mono Channel. For this article, we are using Mono version 0.20. You'll notice that in addition to the Mono packages providing the runtime, C# compiler and class libraries, there are a few other goodies to play with such as the Mono debugger, XSP web server and Monodoc documentation browser.
If you have trouble installing Mono, check out the tutorials offered on the web site.
Mono currently comes packaged with the following components:
C# and Basic language compilers.
VES consisting of a JIT compiler and associated garbage collector, security system, class loader, verifier and threading system. An interpreter is also included.
A set of class libraries written in C# that implements the classes defined in the CLI standard, classes that are part of the .NET FCL, and other Mono-specific classes.
The Mono C# language compiler is mcs. In an interesting programming feat, mcs is written in C#. Since Mono 0.10, mcs even has been able to compile itself. If you are interested in the details of the command-line options, which are compatible with the command-line options provided by Microsoft's C# compiler, a thorough man page is available.
The compiler for Mono's equivalent of Visual Basic.NET, MonoBasic, is mbas. Although not as far in development as the C# compiler, mbas provides enough functionality to experiment a little in Basic.
Two execution environments are included with Mono, mono and mint. mono is a JIT compiler compatible with the CLI's definition of the VES. mint on the other hand, is an interpreter. It is provided as an easy-to-port alternative to mono, which currently runs only on the x86 platform. For the greatest code execution speed, use mono.
A couple interesting utilities also provided with Mono are monodis and pedump. monodis is used to disassemble a compiled assembly and output the corresponding CIL code. It was used to display the sample CIL code for Listing 1. If you are curious to see more of what CIL looks like or to take a peek at what makes up a portable executable, play around with these.
Now that we are familiar with the components of Mono, it is time to try them out. To experiment with the language interaction of Mono, we write a simple class with a single method in C# and call it from a MonoBasic program.
Listing 2 shows the C# library ljlib.cs, and Listing 3 shows the MonoBasic program hello.vb.
The first step is to compile the ljlib.cs into a library. Compiled libraries have the .dll extension, and compiled executables have the .exe extension. To compile to a library, use the -target:library switch in mcs:
[jdq@newton]$ mcs -target:library ljlib.cs Compilation succeeded
This creates the ljlib.dll file, which contains the LJlib namespace and Output class. Now we need to compile the hello.vb program. In order to use the ljlib.dll file we just created, we need to tell the MonoBasic compiler to use it as a reference. We do that with the -r switch:
[jdq@newton]$ mbas -r ./ljlib.dll hello.vb Compilation succeededThe output of mbas is the PE hello.exe. It can be executed with mono:
[jdq@newton]$ mono hello.exe Hello Linux Journal!And there you have it—two languages, C# and MonoBasic, executing on the same runtime and working together. This is a trivial example; however, it does demonstrate the language independence and interoperability of the CLI and hints at the power of Mono as a development platform.
Though still in development, Mono shows promise for greatly benefiting Linux development. With the progress of the last two years as a gauge, the future of Mono should prove to be quite exciting.