使用Mono让.NET程序跨平台运行

??众所周知,Unity3D引擎凭借着强大的跨平台能力而备受开发者的青睐,在跨平台应用开发渐渐成为主流的今天,具备跨平台开发能力对程序员来说就显得特别重要。传统的针对不同平台进行开发的方式常常让开发者顾此失彼,难以保证应用程序在不同的平台都有着相同的、出色的体验,这种情况下寻找到一种跨平台开发的方式将会为解决这个问题找到一种思路。从目前的开发环境来看,Web应该是最有可能成为跨平台开发的神兵利器,可是长期以来Web开发中前端和后端都有各自不同的工作流,虽然现在出现了前端和后端逐渐融合的趋势,可在博主看来想让Web开发变得像传统开发这样简单还需要一定的过渡期。

从Mono到Xamarin

??对Unity3D来说,Mono是实现它跨平台的核心技术。Mono是一个旨在使得.NET在Linux上运行的开源项目。它通过内置的C#语言编译器、CLR运行时和各种类库,可以使.NET应用程序运行在Windows、Linux、FreeBSD等不同的平台上。而在商业领域,Xamarin则实现了用C#编写Android和iOS应用的伟大创举。Windows10发布的时候,微软提出了通用应用UWP的设想,在这种设想下开发者可以直接在最新的Visual Studio中使用C#编写跨平台应用。最近微软收购了Xamarin,这一举措能够保证Xamarin这样的商业项目可以和微软的产品融合地更好。虽然在传统Web开发中Java和PHP目前占据主要优势,可是虽然云计算技术的流行,服务器成本的降低或许会让C#这样优秀的语言更加成熟。我一直坚信技术没有好坏的区别,一切技术问题的核心是人,所以接下来,我们打算追随着跨平台开发的先驱——Java,最早提出的“一次编写、到处运行”的伟大思想来探索C#程序跨平台的可能性。

Mono跨平台的原理

??在提到Mono跨平台的时候,我们首先需要引入公共语言基础(Common Language Infrastructure,CLI)这个概念,CLI是一套ECMA定义的标准,它定义了一个和语言无关的跨体系结构的运行环境,这使得开发者可以用规范定义内各种高级语言来开发软件,并且无需修正即可让软件运行在不同的计算机体系结构上。因此我们可以说跨平台的原理是因为我们定义了这样一个和语言无关的跨体系结构的运行环境规范,只要符合这个规范的应用程序都可以运行在不同的计算机体系结构上,即实现了跨平台。针对这个标准,微软实现了公共语言运行时(Common Language Runtime,CLR),因此CLR是CLI的一个实现。我们熟悉的.NET框架就是一个在CLR基础上采用系统虚拟机的编程平台,它为我们提供了支持多种编程语言如C#、VB.NET、C++、Python等。我们编写的C#程序首先会被C#编译器编译为公共中间语言即CIL或者是MSIL(微软中间语言),然后再由CLR转换为操作系统的原生代码(Native Code)。

??好了,现在我们来回答最开始的问题:Mono为什么能够跨平台。我们回顾.NET程序运行机制可以发现实现.NET跨平台其实需要这三个关键:编译器、CLR和基础类库。在.NET下我们编写一个最简单的“Hello World”都需要mscorlib.dll这个动态链接库,因为.NET框架已经为我们提供了这些,因为在我们的计算机上安装着.NET框架,这是我们编写的应用程序能够在Windows下运行的原因。再回头来看Mono,首先Mono和CLR一样,都是CLI这一标准的实现,所以我们可以理解为Mono实现了和微软提供给我们的类似的东西,因为微软的.NET框架属于商业化闭源产品,所以Mono除了在实现CLR和编译器的同时实现了大量的基础库,而且在某种程度上Mono实现的版本与相同时期.NET的版本有一定的差距,这点使用Unity3D开发游戏的朋友应该深有感触吧!这就决定了我们在将应用程序移植到目标平台时能否实现在目标平台上和当前平台上是否能够具有相同的体验。因为公共中间语言即CIL能够运行在所有实现了CLI标准的环境中,而CLI标准则是和具体的平台或者说CPU无关的,因此只要Mono运行时能够保证CIL的运行,就可以实现应用程序的跨平台。我们可以通过下面这张图来总结下这部分内容:

开发第一个跨平台程序

??下面我们来尝试开发第一个跨平台程序,我们使用Visual Studio或者MonoDevelop编写一个简单的控制台应用程序,为了减少这个程序对平台特性的依赖,我们这里选择System这个命名空间来实现最为基础的Hello World,这意味着我们的应用程序没有使用任何除mscorlib.dll以外的库:

using System;

namespace MonoApplication
{
    class MainClass
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

??因为我们的计算机安装了.NET框架,所以我们编写的这个程序会被C#编译器编译为公共中间语言CIL,然后再由CLR转换为Native Code。通常情况下公共中间语言(CIL)会被存储到.il文件中,可是在这里我们在编译的时候好像并没有看到这个文件的生成啊,这是因为这里生成的可执行文件(.exe)本质上是公共中间语言(CIL)形态的可执行文件。这一点我们可以通过ildasm这个工具来验证,该工具可以帮助我们查看IL代码,通常它位于C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin这个位置。下面是通过这个工具获得的IL代码:

.method public hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // 代码大小       13 (0xd)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "Hello World!"
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  ret
} // end of method MainClass::Main

??可以看到这段代码和我们编写的程序中的Main方法完全对应,关于这段代码的含义,大家可以通过搜索引擎来了解IL代码的语法。因为我们这里想要说明的是,这里生成的可执行文件(.exe)从本质上来讲并非是一个可执行文件。因为它能否执行完全是取决于CPU的,这和我们直接用C++编写的应用程序不同,我们知道不同的编译器如Windows下的VC++和Linux下的GCC都是和硬件紧密相连的,所以我们编译的程序能够在各自的平台直接运行,即CPU是认识这些程序的。可是在.NET这里就不一样了,因为我们通过C#编译器即csc.exe编译出来的文件,其实是一个看起来像可执行文件,实际上却是一个和平台无关、和CPU无关的IL文件。

??那么我们就会感到迷茫了啊,平时我们编译完C#程序双击就可以打开啊,哈哈,现在隆重请出.NET程序的家长公共语言运行时(CLR)。公共语言运行时实际上是程序运行的监管者,程序运行的情况完全由运行时来决定。我们双击这个文件的时候,公共语言运行时会将其加载到内存中,然后由即时编译器(JIT)来识别IL文件,然后由CPU去完成相应的操作。

??所以我们可以这样理解.NET程序跨平台,因为IL文件是一个和平台无关、和CPU无关的、跨平台的文件结构,所以我们只需要在不同的平台上实现这样一个公共语言运行时(CLR)就可以实现在不同的平台上运行同一个程序。但这个过程中,需要有一个C#编译器负责将C#代码转换为IL代码,然后需要有一个公共语言运行时(CLR)来解析IL代码。与此同时,我们在.NET框架下使用了大量的基础类库,这些类库在Windows以外的平台是没有的,所以除了C#编译器和公共语言运行时以外,我们还需要基础类库。现在大家是不是对Mono有了更清楚的认识了呢?没错,Mono所做的事情其实就是我们在讨论的这些事情。这里博主想说说即时编译(JIT)和静态编译(AOT),这两种编译方式我们可以按照”解释型”和”编译型”来理解,为什么Unity3D在iOS平台上做热更新的时候会出现问题呢?这是因为iOS平台考虑到安全性禁止使用JIT即时编译,所以像C#这种需要编译的语言在这里就无计可施了。

??好了,既然我们有Mono这样的工具能够帮助我们实现跨平台开发。那么我们现在就来考虑将这个程序移植到Linux平台,这里以Linux Deepin为例,我们按照C#程序编译的过程来完成这个移植过程:

* 1、将C#程序编译为IL文件:在.NET下我们使用csc.exe这个程序来完成编译,在Mono下我们使用mcs.exe这个程序来完成编译,这个程序在安装完Mono以后在其安装目录内可以找到。我们在命令行下输入命令:

mcs D:\项目管理\CSharp\MonoApplication\MonoApplication\Main.cs
  • 2、这样将生成Main.exe这样一个IL文件,现在我们需要一个运行时来解析它,在.NET下我们使用CLR来完成这个步骤,在Mono下我们使用mono.exe这个文件来完成这个步骤。我们在命令行下输入下列命令:
mono D:\项目管理\CSharp\MonoApplication\MonoApplication\Main.exe

我们可以看到命令行下输出了我们期望的Hello World,这意味着我们编写的程序现在运行在Mono中了,实际上在Windows下由Mono提供的C#编译器mcs.exe编译的IL文件双击是可以直接运行的,因为我们的计算机上安装了CLR,它作为.NET的一部分内置在我们的计算机中。由此我们会发现一个问题,我们这里的跨平台实际上是编译器、运行时和基础类库这三部分的跨平台,这意味着我们在Linux下运行.NET程序是需要Mono提供支持的。因为在这里我无法在Linux离线安装Mono,所以Linux下运行.NET程序的验证需要等博主以后有时间再来更新啦!可是我们可以想象到,通过C#编译器编译得到的可执行文件在Linux下是无法正常运行的,因为通常情况下Windows程序在Linux下运行是需要虚拟机环境或者Wine这样的软件来支持的,显然让这样一个Windows程序运行在Linux环境下是因为我们在Linux下安装了Mono。

谈谈Mono跨平台以后

??好了,到现在为止我们基本理清了Mono跨平台的原理。我们知道微软的技术体系在发展过程中因为某些历史遗留问题,.NET程序在不同的Windows版本中的兼容性有时候会出现问题,虽然微软宣布Windows XP停止维护,我们编写Windows应用程序的时候可以忽略对Windows XP版本的支持,可是因为国内用户不喜欢在线更新补丁的这种普遍现状,所以假如让用户在安装程序的时候先去安装.NET框架一定会降低用户体验,其次.NET框架会增加应用程序安装包的大小,所以我们需要一种能够让我们开发的.NET应用程序在脱离微软的这套技术体系时,同时能够安全、稳定的运行,所以我们这里考虑借助Mono让.NET程序脱离.NET框架运行。

??首先,我们来说说.NET程序为什么能够脱离.NET框架运行,我们注意到Mono提供了一个Mono运行时,所以我们可以借助这样一个运行时来运行编译器生成的IL代码。我们继续以Hello World为例,我们在使用Mono编译出IL代码以后需要使用Mono运行时来解析IL代码,所以假如我们可以编写一个程序来调用Mono运行时就可以解决这个问题。在这个问题中,其实精简应用程序安装包的大小从本质上来讲就是解决基础类库的依赖问题,因为Mono实现了.NET框架中大部分的基础类库,所以移植.NET应用程序的关键是基础类库的移植,比如WinForm在Linux下的解决方案是GTK,这些细节在考虑跨平台的时候都是非常重要的问题。

小结

??本文从Mono跨平台的原理说起,探讨了.NET应用程序跨平台的可能性和具体实现。跨平台是一个涉及到非常多内容的话题,我个人理解的跨平台是要编写跨平台的代码,这意味着我们在编写程序的时候需要考虑减少对平台特性的移植,比如说Linq是一个非常棒的特性,可是这个特性离开了Windows、离开了.NET就没有办法得到保证,所以如果要让使用了Linq的应用程序跨平台就会是一件非常麻烦的事情!在不同的平台间保持相同的体验很难,就像我们编写的Web程序在不同的浏览器间都有着不一样的表现,所以跨平台这个问题我们就抱着学习的态度来研究吧!

时间: 2024-10-13 05:18:28

使用Mono让.NET程序跨平台运行的相关文章

java拾遗4----一个简单java程序的运行全过程

简单说来,一个java程序的运行需要编辑源码.编译生成class文件.加载class文件.解释或编译运行class中的字节码指令. 下面有一段简单的java源码,通过它来看一下java程序的运行流程: 1 class Person 2 3 { 4 5 private String name; 6 7 private int age; 8 9 10 11 public Person(int age, String name){ 12 13 this.age = age; 14 15 this.na

跨平台运行的服务插件

开源一个跨平台运行的服务插件 - TaskCore.MainForm 本次将要很大家分享的是一个跨平台运行的服务插件 - TaskCore.MainForm,此框架是使用.netcore来写的,现在netcore已经支持很多系统平台运行了,所以将以前的Task.MainForm改良成跨平台的服务共大家使用和相互交流:本来这篇应该分享的是nginx+iis+redis+Task.MainForm构建分布式架构后续文章,但使用.netcore来定义一个服务插件和跨平台测试经过让我既兴奋又头痛,不说了

Java为什么能跨平台运行

 因为java程序编译之后的代码不是能被硬件系统直接运行的代码,而是一种“中间码”--字节码.不同的硬件平台上装有不同的java虚拟机(JVM),由JVM来把字节码再翻译成所对应的硬件平台能够执行的代码,因此java可以跨平台运行.

开源一个跨平台运行的服务插件 - TaskCore.MainForm

本次将要很大家分享的是一个跨平台运行的服务插件 - TaskCore.MainForm,此框架是使用.netcore来写的,现在netcore已经支持很多系统平台运行了,所以将以前的Task.MainForm改良成跨平台的服务共大家使用和相互交流:本来这篇应该分享的是nginx+iis+redis+Task.MainForm构建分布式架构后续文章,但使用.netcore来定义一个服务插件和跨平台测试经过让我既兴奋又头痛,不说了下次再分享分布式后续文章吧:那么开始今天的分享内容,希望各位多多支持:

1.3.2 java程序的运行机制和jvm

java语言比较特殊,由java语言编写的程序需要经过编译步骤,但这个编译步骤并不会生成特定平台的机器码,而是生成一种与平台无关的字节码(也就是*.class文件).当然,这种字节码不是可执行性的,必须使用java解释器来解释执行.因此,我们可以认为:java语言既是编译型语言,也是解释型语言.或者说,java语言既不是纯粹的编译型语言,也不是纯粹的解释型语言.java程序的执行过程必须经过先编译,后解释两个步骤.如图1.1所示. java语言里负责解释执行字节码文件的是java虚拟机,既 JV

Java程序的运行机制和JVM

1. Java语言比较特殊, 由Java编写的程序需要经过编译步骤,但这个编译步骤不会产生特定平台的机器码,而是生成一种与平台无关的字节码(也就是.class文件).这种字节码不是可执行性的,必须使用Java解释器来解释执行.因此可以认为: Java语言既是编译型语言,也是解释型语言. 或者说,Java语言既不是纯粹的编译型语言,也不是纯粹的解释型语言. Java程序的执行过程,必须经过先编译,后解释两个步骤. 2. Java语言里,负责解释执行字节码文件的是Java虚拟机,即JVM(Java

.Net Core应用程序跨平台应用研究-HelloArm(串口篇)

引言 为了验证采用dotnet core技术开发的物联网设备数据采集接入服务应用是否能在高性价比的linux嵌入式平台运行,针对dotnet core应用程序进行嵌入式linux环境的发布部署运行验证研究. 硬件环境 硬件系统经过对比筛选,选用了友善之臂出品的NanoPC-T3 Plus.该控制板为掌上型嵌入式Linux系统迷你PC,采用64位三星arm CPU,具有完善的硬件接口和驱动支持,大于1G的内存和8G以上的eMMC闪存,具有较高的性能价格比. NanoPC-T3 Plus是友善之臂专

程序的运行顺序[转]

关於程序的运行顺序 我们知道 Linux 是多人多工的环境,由 top 的输出结果我们也发现, 系统同时间有非常多的程序在运行中,只是绝大部分的程序都在休眠 (sleeping) 状态而已. 想一想,如果所有的程序同时被唤醒,那么 CPU 应该要先处理那个程序呢?也就是说,那个程序被运行的优先序比较高? 这就得要考虑到程序的优先运行序 (Priority) 与 CPU 排程罗! CPU 排程与前一章的例行性工作排程并不一样. CPU 排程指的是每支程序被 CPU 运行的演算守则, 而例行性工作排

linux程序后台运行

linux命令后台运行  有两种方式: 1. command & : 后台运行,你关掉终端会停止运行   2. nohup command & : 后台运行,你关掉终端也会继续运行 一. 简介     Linux/Unix 区别于微软平台最大的优点就是真正的多用户,多任务.因此在任务管理上也有别具特色的管理思想.我们知道,在 Windows 上面,我们要么让一个程序作为服务在后台一直运行,要么停止这个服务.而不能让程序在前台后台之间切换.而 Linux 提供了 fg 和bg 命令,让你轻松