1. 项目缘起为什么要在银河麒麟上搞Mono最近接手了一个老项目技术栈是.NET Framework 4.5一堆WinForm和WCF服务典型的“祖传代码”。客户的新服务器采购了一批搭载银河麒麟操作系统的国产化终端要求把服务迁移上去。第一反应当然是上.NET Core或者.NET 5但一评估代码里大量用了System.Web、Remoting这些“古董”直接迁移等于重写时间和成本都不允许。这时候Mono这个老伙计就进入了视野。Mono是一个跨平台的.NET Framework开源实现它能让原本为Windows设计的.NET应用在Linux、macOS等系统上运行。对于这种历史包袱重、又必须快速完成国产化适配的场景Mono几乎是唯一可行的过渡方案。银河麒麟作为国内主流的Linux发行版基于Debian或CentOS理论上Mono是支持的但实际操作起来你会发现官方仓库的版本可能老旧依赖关系需要手动处理网络环境也可能受限比如内网离线部署每一步都可能藏着坑。这篇文章就是我最近在银河麒麟V10SP1桌面版上从零搭建一套完整、可用的Mono开发与运行环境的完整记录。我会把安装、配置、验证、以及开发工具链搭建的每一步都拆开讲清楚特别是那些文档里不会写但实际部署时一定会遇到的“坑”。目标很明确让你拿到这份指南就能在自己的麒麟系统上复现一个稳定的Mono环境无论是为了运行旧应用还是进行简单的跨平台开发。2. 环境准备与核心概念澄清动手之前我们先理清几个关键点这能避免后续很多不必要的困惑。2.1 银河麒麟版本与架构确认银河麒麟有多个版本常见的有桌面版和服务器版底层可能基于Ubuntu KylinDebian系或NeoKylinCentOS系。我们这次以**银河麒麟桌面操作系统V10SP1**为例它属于Debian系使用APT包管理器。如果你的系统是服务器版或基于CentOS部分命令如apt-get需换成yum或dnf需要调整。首先打开终端确认系统信息cat /etc/os-release uname -m关键要看两点1.ID字段是否包含kylin2. 架构是x86_64还是aarch64ARM。Mono对这两种主流架构都提供支持但安装包不同。本文假设为x86_64架构。2.2 Mono、.NET Framework与.NET Core/.NET 5的关系很多人容易混淆这三者这里必须掰扯清楚.NET Framework微软推出的原始框架只能在Windows上运行。你的老C#项目大概率基于它。Mono一个独立的、开源的、跨平台的.NET Framework兼容实现。它实现了.NET Framework的类库和CLR公共语言运行时目标是让.NET Framework应用能跑在非Windows系统上。你可以把它看作.NET Framework的一个“Linux移植版”。.NET Core / .NET 5微软官方推出的跨平台、开源的现代化.NET实现。它和.NET Framework是并行的关系并非完全兼容。它设计更轻量性能更好是未来方向。结论如果你的目标是不修改或极少修改代码让旧的.NET Framework应用在银河麒麟上跑起来那么你应该选择Mono。如果你的应用可以重写或迁移那么应该选择**.NET 6/7/8**。2.3 安装策略选择包管理器 vs 源码编译安装Mono通常有两种方式使用官方仓库推荐Mono项目为Debian/Ubuntu等系统维护了APT仓库。好处是安装方便自动处理依赖后续更新也容易。这是我们的首选方案。源码编译极度不推荐新手使用。过程繁琐需要安装GCC、make、autoconf等一整套工具链耗时长且极易因依赖问题失败。仅在仓库版本不满足特定需求如需要某个未发布的补丁时才考虑。我们将采用第一种方式通过添加Mono官方仓库进行安装。即使是在内网环境也可以参照此方法在能联网的机器上下载好所有依赖包然后进行离线安装。3. 分步安装Mono运行时与开发环境接下来进入实操环节。请确保你拥有系统的sudo权限。3.1 步骤一添加Mono官方GPG密钥与软件源银河麒麟默认的软件源里可能没有Mono或者版本非常旧。我们需要添加Mono项目的官方仓库。首先安装一些必要的工具并信任Mono项目的GPG密钥sudo apt update sudo apt install -y gnupg ca-certificates sudo gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mono-official-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF这条命令从Ubuntu密钥服务器获取Mono项目的公钥并将其存入系统密钥环。如果网络不通可以多试几次或者搜索其他可用的密钥服务器地址。接下来添加Mono的稳定版仓库源。对于Debian 10Buster/ Ubuntu 20.04及类似版本的系统银河麒麟V10基于此命令如下echo deb [signed-by/usr/share/keyrings/mono-official-archive-keyring.gpg] https://download.mono-project.com/repo/debian stable-buster main | sudo tee /etc/apt/sources.list.d/mono-official-stable.list注意stable-buster中的buster是Debian 10的代号。请根据你的系统基础版本调整。如果不确定可以尝试cat /etc/debian_version或查看/etc/os-release中的VERSION_CODENAME。3.2 步骤二安装Mono运行时更新本地软件包索引并安装Mono的完整运行时sudo apt update sudo apt install -y mono-completemono-complete是一个元数据包它会安装Mono运行时、编译器、基础类库以及Gtk#等几乎所有常用组件适合大多数开发和生产场景。安装过程可能会比较长因为它要下载和安装上百个包。安装完成后验证是否成功mono --version如果成功你会看到类似下面的输出包含了Mono的版本号如6.12.x、JIT编译器版本等信息。这证明Mono运行时已经就绪。3.3 步骤三安装开发工具可选但推荐如果你不仅仅是要运行应用还需要在银河麒麟上编译C#代码那么需要安装开发工具。sudo apt install -y monodevelopmonodevelop是Mono官方的集成开发环境功能类似老版本的Visual Studio。不过实话实说在当今VS Code横行的时代Monodevelop的体验已经有些落伍了。我更推荐使用Visual Studio Code。3.3.1 配置Visual Studio Code作为C#开发环境安装VS Code从官网下载.deb包或者通过命令行安装如果系统源里有sudo apt update sudo apt install -y wget gpg wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor packages.microsoft.gpg sudo install -D -o root -g root -m 644 packages.microsoft.gpg /etc/apt/keyrings/packages.microsoft.gpg echo deb [archamd64 signed-by/etc/apt/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main | sudo tee /etc/apt/sources.list.d/vscode.list sudo apt update sudo apt install -y code安装C#扩展启动VS Code点击左侧活动栏的扩展图标搜索“C#”安装由Microsoft发布的“C#”扩展。这个扩展名为ms-dotnettools.csharp它提供了对.NET Core和Mono项目的智能感知、调试等强大支持。配置OmniSharp使用Mono这是关键一步。默认情况下C#扩展的OmniSharp服务器可能试图使用.NET Core SDK来分析项目但对于传统的.NET Framework项目我们需要它使用Mono。在VS Code中按下Ctrl Shift P打开命令面板。输入并选择 “Preferences: Open User Settings (JSON)”。在打开的settings.json文件中添加或修改如下配置{ omnisharp.useGlobalMono: always, omnisharp.monoPath: /usr/bin // Mono可执行文件所在目录通常不需要改 }保存文件。这样OmniSharp就会强制使用我们系统安装的Mono来加载和解析项目对于csproj文件里引用的System.Windows.Forms等传统库才能正确识别。4. 验证与测试让你的第一个C#程序跑起来环境装好了是骡子是马得拉出来溜溜。我们通过几个不同层次的测试来确保环境完全可用。4.1 测试一经典的“Hello World”控制台程序创建一个最简单的C#文件cat hello.cs EOF using System; public class HelloWorld { public static void Main(string[] args) { Console.WriteLine(Hello, Galaxy Kylin with Mono!); } } EOF使用Mono的编译器mcs进行编译mcs是Mono的C#编译器mcs hello.cs这会在当前目录生成一个hello.exe的可执行文件。注意这个.exe文件并不是Windows原生PE格式而是一个包含CIL中间语言的程序集需要Mono运行时来执行。使用Mono运行时执行它mono hello.exe如果终端成功打印出“Hello, Galaxy Kylin with Mono!”那么恭喜你最基础的编译和运行环境已经通了。4.2 测试二带图形界面Gtk#的程序很多老的.NET桌面程序是WinForm的而Mono在Linux上主要用Gtk#作为GUI工具包的实现。我们来测试一下图形界面支持。首先确保安装了Gtk#的开发库通常mono-complete已包含sudo apt install -y gtk-sharp3创建一个简单的Gtk#程序cat gtk-hello.cs EOF using Gtk; using System; class HelloWorld { static void Main() { Application.Init(); Window window new Window(Mono on Kylin); window.SetDefaultSize(300, 200); window.DeleteEvent (o, args) Application.Quit(); Button button new Button(Click Me!); button.Clicked (sender, e) Console.WriteLine(Button clicked from Gtk#!); window.Add(button); window.ShowAll(); Application.Run(); } } EOF编译这个程序。注意需要引用gtk-sharp程序集mcs -pkg:gtk-sharp-3.0 gtk-hello.cs运行mono gtk-hello.exe如果一切正常会弹出一个带有“Click Me!”按钮的窗口。点击按钮终端里会输出文字。这个测试验证了Mono的图形界面库和事件机制工作正常。4.3 测试三运行现有的.NET Framework可执行文件.exe这是最终目的。你可以尝试将一个在Windows上编译好的、简单的.NET Framework控制台程序.exe文件拷贝到银河麒麟上直接使用mono YourApp.exe来运行。重要提示兼容性不是100%Mono虽然实现了大部分.NET Framework但并非100%兼容特别是涉及Windows原生API调用如P/Invoke调用user32.dll、COM组件、WPFWindows Presentation Foundation等部分在Linux上无法运行。你的应用如果大量依赖这些迁移会很困难。配置文件注意App.config或Web.config文件。Mono有自己的配置文件系统有时需要调整。特别是数据库连接字符串如SQL Server的驱动可能需要改为npgsqlfor PostgreSQL或MySQL连接器。文件路径代码中所有硬编码的Windows风格路径如C:\Users\...都需要改为Linux风格如/home/username/...或使用Path.Combine等跨平台方法。5. 进阶配置与生产环境考量如果只是跑通Demo那太简单了。要让老系统真正稳定跑在生产环境还有一堆细节要处理。5.1 配置Mono运行时选项通过环境变量或命令行参数可以调整Mono运行时的行为。一些有用的选项垃圾回收器GC选择Mono默认使用Boehm GC保守式对于服务端应用建议使用SGen GC分代式性能更好。可以通过环境变量设置export MONO_ENV_OPTIONS--gcsgen # 或者运行程序时指定 mono --gcsgen your-app.exe设置线程池大小对于Web服务如托管ASP.NET应用可能需要调整线程池。export MONO_THREADS_PER_CPU50启用JIT优化mono --optimizeall your-app.exe5.2 将Mono应用部署为系统服务以Systemd为例在服务器上我们通常需要让应用以服务形式在后台运行开机自启。银河麒麟V10使用systemd。假设你的应用目录在/opt/myapp主程序是MyService.exe。创建一个服务用户可选但推荐sudo useradd -r -s /bin/false myappuser sudo chown -R myappuser:myappuser /opt/myapp创建Systemd服务单元文件sudo nano /etc/systemd/system/myapp.service写入以下内容根据实际情况修改[Unit] DescriptionMy .NET App on Mono Afternetwork.target [Service] Typesimple Usermyappuser WorkingDirectory/opt/myapp ExecStart/usr/bin/mono /opt/myapp/MyService.exe EnvironmentMONO_ENV_OPTIONS--gcsgen Restarton-failure RestartSec10 [Install] WantedBymulti-user.target启用并启动服务sudo systemctl daemon-reload sudo systemctl enable myapp.service sudo systemctl start myapp.service sudo systemctl status myapp.service # 查看状态5.3 性能监控与调试查看进程信息使用ps aux | grep mono查看Mono进程的资源占用。生成堆栈跟踪如果应用无响应可以向Mono进程发送SIGUSR2信号使其在标准错误输出上打印所有线程的堆栈跟踪。kill -USR2 PID使用lttng进行性能分析Mono集成了LTTng进行跟踪。需要先安装lttng工具然后可以收集详细的JIT、GC事件进行分析。这属于高级话题在遇到复杂性能问题时可以研究。6. 常见问题与故障排查踩坑实录这部分是我在实际部署中遇到的和社区里常见的问题汇总。6.1 安装失败GPG密钥添加失败或仓库无法访问现象执行sudo apt update时提示NO_PUBKEY错误或仓库地址连接超时。排查网络问题银河麒麟某些版本或定制环境可能网络受限。尝试ping download.mono-project.com。如果无法访问需要配置代理或在能联网的机器上下载离线包。密钥服务器问题hkp://keyserver.ubuntu.com:80可能被屏蔽。可以尝试其他服务器如hkp://pgp.mit.edu:80或者直接下载密钥文件wget -O - https://download.mono-project.com/repo/xamarin.gpg | sudo apt-key add -注意apt-key命令已逐渐被弃用但在一些旧系统上仍可用。更规范的做法如步骤3.1所示将密钥放入/usr/share/keyrings/。离线安装方案在能联网的、相同系统版本的机器上使用apt download命令下载mono-complete及其所有依赖的.deb包然后拷贝到内网机器上用dpkg -i *.deb安装。注意处理依赖顺序。6.2 运行时报错缺少依赖库.so文件现象运行mono your.exe时报错DllNotFoundException或Unable to load shared library xxx.so。原因你的应用或它引用的某个Native库依赖了特定的系统共享库如libgdiplus用于图形操作libsqlite用于数据库。解决使用ldd命令检查Mono运行时本身或你的Native库依赖了哪些.so文件然后安装对应的系统包。# 查找缺失的库 ldd /usr/bin/mono | grep not found # 安装常见的依赖例如libgdiplus是很多WinForm应用需要的 sudo apt install -y libgdiplus # 如果是数据库驱动问题安装对应的库如MySQL sudo apt install -y libmysqlclient-dev6.3 中文显示或输入法问题现象GUI程序中文显示为方框或者无法输入中文。解决字体确保系统安装了中文字体。银河麒麟通常自带如果没有可以安装sudo apt install -y fonts-wqy-microhei fonts-wqy-zenhei输入法框架对于Gtk#程序需要确保输入法框架如fcitx或ibus已安装并正确配置。在终端运行im-config可以配置输入法。环境变量在启动脚本或systemd服务文件中设置正确的本地化环境变量有时能解决问题EnvironmentLANGzh_CN.UTF-8 EnvironmentLC_ALLzh_CN.UTF-86.4 应用程序无法访问特定端口或文件现象应用尤其是Web服务启动失败提示“权限被拒绝”。原因在Linux上1024以下的端口需要root权限才能绑定。用普通用户运行的服务无法绑定80或443端口。解决使用高端口修改应用配置使用1024以上的端口如8080, 8443。端口转发使用iptables或nftables将80端口的流量转发到应用的实际端口。能力机制Capabilities给Mono解释器赋予CAP_NET_BIND_SERVICE能力较复杂不推荐新手sudo setcap cap_net_bind_serviceep /usr/bin/mono注意这存在安全风险。7. 迁移后的维护与展望费了这么大劲把环境搭起来应用跑起来了但这只是第一步。长期来看你需要一个维护策略。首先监控是必须的。除了系统级的监控CPU、内存、磁盘还要关注Mono进程本身的健康状况。可以写一个简单的健康检查接口如果应用是Web服务或者定期检查日志文件。Mono的运行时日志可以通过环境变量MONO_LOG_LEVEL来控制比如export MONO_LOG_LEVELinfo。其次备份和回滚方案。每次部署新版本前备份整个应用目录和配置文件。Mono环境本身相对稳定但应用更新可能引入问题。准备好快速回滚到上一个已知正常版本的方法。最重要的明确Mono的定位。它应该是一个过渡方案而不是终极解决方案。在应用稳定运行于Mono环境的同时就应该着手评估和规划向**.NET 6/8**的迁移。可以从非核心的、依赖最少的功能模块开始尝试迁移积累经验。.NET Core的跨平台性是原生的性能、可维护性和社区支持都远胜于Mono。这次在银河麒麟上部署Mono的经历让我再次体会到技术债总是要还的。Mono是一座宝贵的桥梁它让我们有机会在不重写整个系统的情况下迈出国产化、跨平台的第一步。但走过这座桥之后前方更现代、更高效的.NET生态系统才是我们最终应该抵达的彼岸。整个过程里耐心阅读日志、善用系统工具如strace,ldd排查依赖以及保持一个可随时重建的清晰安装文档是比任何具体命令都更重要的经验。