惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tailwind CSS Blog
大猫的无限游戏
大猫的无限游戏
L
LINUX DO - 热门话题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
雷峰网
雷峰网
aimingoo的专栏
aimingoo的专栏
博客园_首页
MongoDB | Blog
MongoDB | Blog
V
V2EX
GbyAI
GbyAI
量子位
Microsoft Azure Blog
Microsoft Azure Blog
有赞技术团队
有赞技术团队
G
Google Developers Blog
云风的 BLOG
云风的 BLOG
B
Blog
Microsoft Security Blog
Microsoft Security Blog
S
SegmentFault 最新的问题
O
OpenAI News
N
News and Events Feed by Topic
博客园 - Franky
爱范儿
爱范儿
Forbes - Security
Forbes - Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V2EX - 技术
V2EX - 技术
Application and Cybersecurity Blog
Application and Cybersecurity Blog
N
News and Events Feed by Topic
N
News | PayPal Newsroom
Schneier on Security
Schneier on Security
Cloudbric
Cloudbric
Security Archives - TechRepublic
Security Archives - TechRepublic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Recent Commits to openclaw:main
Recent Commits to openclaw:main
人人都是产品经理
人人都是产品经理
P
Privacy International News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog RSS Feed
阮一峰的网络日志
阮一峰的网络日志
D
DataBreaches.Net
Last Week in AI
Last Week in AI
罗磊的独立博客
Spread Privacy
Spread Privacy
Recent Announcements
Recent Announcements
The Cloudflare Blog
Google DeepMind News
Google DeepMind News
AWS News Blog
AWS News Blog
The Register - Security
The Register - Security
Y
Y Combinator Blog
J
Java Code Geeks
I
Intezer

元视角

.NET 生态下的 Agent 框架选型:从 ReAct 到原生推理 - 元视角 从「能用」到「好用」:LLM 流式响应实现方式的探索之路 - 元视角 当我用 2000 条聊天记录,让 AI 为我画一幅自画像 - 元视角 基于 Supabase 的 AI 应用开发探索 - 元视角 微博 × MCP:社交媒体新玩法解锁 - 元视角 四点钟海棠花未眠 - 元视角 Semantic Kernel × MCP:智能体的上下文增强探索 - 元视角 基于 K-Means 聚类分析实现人脸照片的快速分类 - 元视角 容器技术驱动下的代码沙箱实践与思考 - 元视角 温故而知新:后端通用查询方案的再思考 - 元视角 浅议 CancellationToken 在前后端协同取消场景中的应用 - 元视角 Semantic Kernel 视角下的 Text2SQL 实践与思考 - 元视角 关于 ChatGPT 的流式传输,你需要知道的一切 - 元视角 RAG 的是与非、Rewrite 和 Rerank - 元视角 使用 EFCore 和 PostgreSQL 实现向量存储及检索 - 元视角 基于 LLaMA 和 LangChain 实践本地 AI 知识库 - 元视角 使用 llama.cpp 在本地部署 AI 大模型的一次尝试 - 元视角 如何为 Git 配置多个 SSH Key - 元视角 C# 使用 LibUsbDotNet 实现 USB 设备检测 - 元视角 基于 C# 实现样式与数据分离的打印方案 - 元视角 基于 SVG 的图形交互方案实践 - 元视角 前端视频播放技术概览 - 元视角 温故而知新,再话 Python 动态导入 - 元视角 后 GPT 时代,NLP 不存在了? - 元视角 视频是不能 P 的系列:使用 Milvus 实现海量人脸快速检索 - 元视角 GDI+下字体大小自适应方案初探 - 元视角 小爱音箱集成 ChatGPT 的不完全教程 - 元视角 程序员视角下的三体世界随想 - 元视角 关于 Docker 容器配置信息的渐进式思考 - 元视角 在 Docker 容器内集成 Crontab 定时任务 - 元视角 为你的服务器集成 LDAP 认证 - 元视角 似花还似非花 - 元视角 视频是不能 P 的系列:使用 Dlib 实现人脸识别 - 元视角 浅议分布式链路追踪与日志的整合 - 元视角 关于 Git 大文件上传这件小事 - 元视角 .NET 进程内队列 Channel 的入门与应用 - 元视角 使用 Fody 实现 .NET 的静态编织 - 元视角 .NET Core + ELK 搭建可视化日志分析平台(下) - 元视角 聊一聊前端图片懒加载背后的故事 - 元视角 支持外部链接跳转的 Vue Router 扩展实现 - 元视角 视频是不能 P 的系列:OpenCV 和 Dlib 实现表情包 - 元视角 不得不说的 ASP.NET Core 集成测试 - 元视角 再议 DDD 视角下的 EFCore 与 领域事件 - 元视角 Vue.js 前端项目容器化部署实践极简教程 - 元视角 再见,人间四月天 - 元视角 Python 图像风格化迁移助力画家梦想 - 元视角 利用 ASP.NET Core 中的标头传播实现分布式链路追踪 - 元视角 利用 gRPC 实现文件的上传与下载 - 元视角 七种武器:延迟队列的原理和实现总结 - 元视角 gRPC 流式传输极简入门指南 - 元视角 Envoy 集成 Jaeger 实现分布式链路追踪 - 元视角 浅议非典型 Web 应用场景下的身份认证 - 元视角 gRPC 借助 Any 类型实现接口的泛化调用 - 元视角 分布式丛林探险系列之 Redis 集群模式 - 元视角 分布式丛林探险系列之 Redis 主从复制模式 - 元视角 通过 Python 预测 2021 年双十一交易额 - 元视角 gRPC 搭配 Swagger 实现微服务文档化 - 元视角 SSL/TLS 加密传输与数字证书的前世今生 - 元视角 使用 Python 自动识别防疫健康码 - 元视角 你不可不知的容器编排进阶技巧 - 元视角 ASP.NET Core 搭载 Envoy 实现 gRPC 服务代理 - 元视角 再话 AOP,从简化缓存操作说起 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT) - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的监控预警 - 元视角 ASP.NET Core 搭载 Envoy 实现微服务的反向代理 - 元视角 ASP.NET Core gRPC 打通前端世界的尝试 - 元视角 EFCore 实体命名约定库:EFCore.NamingConventions - 元视角 ASP.NET Core gRPC 集成 Polly 实现优雅重试 - 元视角 ASP.NET Core gRPC 健康检查的探索与实现 - 元视角 ASP.NET Core gRPC 拦截器的使用技巧分享 - 元视角 SnowNLP 使用自定义语料进行模型训练 - 元视角 使用 HttpMessageHandler 实现 HttpClient 请求管道自定义 - 元视角 ABP vNext 的实体与服务扩展技巧分享 - 元视角 ABP vNext 对接 Ant Design Vue 实现分页查询 - 元视角 源代码探案系列之 .NET Core 跨域中间件 CORS - 元视角 源代码探案系列之 .NET Core 限流中间件 AspNetCoreRateLimit - 元视角 源代码探案系列之 .NET Core 并发限制中间件 ConcurrencyLimiter - 元视角 通过 EmbededFileProvider 实现 Blazor 的静态文件访问 - 元视角 低代码,想说爱你不容易 - 元视角 记一次失败的 ThoughtWorks 面试经历 - 元视角 从 C# 1.0 到 C# 9.0,历代 C# 语言特性一览 - 元视角 通过 Python 分析 2020 年全年微博热搜数据 - 元视角 基于 Python 和 Selenium 实现 CSDN 一键三连自动化 - 元视角 使用多线程为你的 Python 爬虫提速的 N 种姿势,你会几种? - 元视角 实现网页长截图的常见思路总结 - 元视角 温故而知新,由 ADO.NET 与 Dapper 所联想到的 - 元视角 视频是不能 P 的系列:OpenCV 人脸检测 - 元视角 作为技术宅的我,是这样追鬼滅の刃的 - 元视角 使用 Python 抽取《半泽直树》原著小说人物关系 - 元视角 厉害了!打工人用 Python 分析西安市职位信息 - 元视角 使用 dotTrace 对 .NET 应用进行性能分析与优化 - 元视角 一道 HashSet 面试题引发的蝴蝶效应 - 元视角 基于选项模式实现.NET Core 的配置热更新 - 元视角 Dapper.Contrib 在 Oracle 环境下引发 ORA-00928 异常问题的解决 - 元视角 .NET Core 中对象池(Object Pool)的使用 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(下):EventBus 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(中):RabbitMQ 篇 - 元视角 利用 MySQL 的 Binlog 实现数据同步与订阅(上):基础篇 - 元视角 记一次从已损坏的 Git 仓库中找回代码的经历 - 元视角 .NET Core 原生 DI 扩展之属性注入实现 - 元视角
使用 Mono 让.NET 程序跨平台运行 - 元视角
飞鸿踏雪 · 2016-03-06 · via 元视角

众所周知,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

在Mono中运行.NET程序 在Mono中运行.NET程序

我们可以看到命令行下输出了我们期望的 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 程序在不同的浏览器间都有着不一样的表现,所以跨平台这个问题我们就抱着学习的态度来研究吧!