时间能否重来一次 發表於 2025-3-1 09:06:19

.NET适配HarmonyOS进展的方法

<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">1. 前言</a></li><li><a href="#_label1">2. 项目状态</a></li><li><a href="#_label2">3. 运行时</a></li><li><a href="#_label3">4. NativeAOT</a></li><li><a href="#_label4">5. 已知问题</a></li><ul class="second_class_ul"><li><a href="#_lab2_4_0">5.1 syscall限制 (已解决)</a></li><li><a href="#_lab2_4_1">5.2 mmap申请虚拟内存过大(已解决)</a></li><li><a href="#_lab2_4_2">5.3 ICU,OpenSSL等第三方库缺失(已解决)</a></li><li><a href="#_lab2_4_3">5.4 ICU初始化失败(已解决)</a></li><li><a href="#_lab2_4_4">5.5 NativeAOT如何跨平台编译 (Windows平台已解决)</a></li><li><a href="#_lab2_4_5">5.6 无法调用Marshal.GetDelegateForFunctionPointer相关函数</a></li></ul><li><a href="#_label5">6. 如何修改NativeAOT代码</a></li><ul class="second_class_ul"></ul><li><a href="#_label6">7.相关链接</a></li><ul class="second_class_ul"></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>1. 前言</h2>
<p>目前国产化系统浪潮下,适配鸿蒙是中国软件大势所趋,.NET作为最适合开发客户端语言之一,适配鸿蒙系统(HarmonyOS Next)是目前.NET开发者最关心的事情。我目前业余时间正在移植Avalonia到HarmonyOS,去年在.NET Conf CN上分享过,目前又取得一点进展,所以本文把所有问题进行整合与大家进行分享。</p>
<p class="maodian"><a name="_label1"></a></p><h2>2. 项目状态</h2>
<p>目前<strong>.NET可以成功在HarmonyOS Next上运行</strong>。</p>
<p>Avalonia移植项目在真机上也可以运行,本文主要探讨.NET适配相关工作。</p>
<p class="maodian"><a name="_label2"></a></p><h2>3. 运行时</h2>
<p>自HarmonyOS 5.0.0(12)起,禁止匿名内存申请可执行权限,<strong>除系统内置的JavaScript引擎外,其他虚拟机不能使用Jit功能</strong>,所以<strong>无法将CoreCLR接入到鸿蒙系统中</strong>,而最新版的Mono虽然支持解释执行,但是<strong>由于性能问题也不会接入Mono到鸿蒙系统</strong>,最终只能<strong>选择接入NativeAOT运行时</strong>。</p>
<p class="maodian"><a name="_label3"></a></p><h2>4. NativeAOT</h2>
<p>支撑鸿蒙可以接入NativeAOT的原理是鸿蒙系统兼容libc是musl的Linux系统的动态库(.so)。而.NET的RID支持linux-musl-arm64/linux-musl-x64,所以理论上可以将.NET程序编译为原生的Linux动态库(.so),然后在鸿蒙的原生项目中,通过dlopen以及dlsym等函数调用C#中的入口函数。</p>
<p>而C#调用鸿蒙api则通过P/Invoke调用鸿蒙的NDK,而ArkUI的TypeScript api则通过NDK中的napi调用。</p>
<p>具体做法可以参考我正在做的Avalonia移植项目:&nbsp;<a href="https://github.com/OpenHarmony-NET/OpenHarmony.Avalonia" rel="external nofollow" target="_blank">https://github.com/OpenHarmony-NET/OpenHarmony.Avalonia</a></p>
<p class="maodian"><a name="_label4"></a></p><h2>5. 已知问题</h2>
<p class="maodian"><a name="_lab2_4_0"></a></p><h3>5.1 syscall限制 (已解决)</h3>
<p>鸿蒙系统使用了seccomp限制危险的syscall调用。标准posix下,如果系统不支持某个syscall则返回错误码,而seccomp非常激进,如果调用了非法的sycall则直接杀掉进程。.NET的运行时初始化时,会调用__NR_get_mempolicy系统调用对numa支持进行检查,而这个系统调用不在鸿蒙的seccomp白名单中,所以导致直接宕机。</p>
<p>鸿蒙系统中seccomp的系统调用白名单如下:<a href="https://gitee.com/openharmony/startup_init/blob/master/services/modules/seccomp/seccomp_policy/app.seccomp.policy" rel="external nofollow" target="_blank">https://gitee.com/openharmony/startup_init/blob/master/services/modules/seccomp/seccomp_policy/app.seccomp.policy</a></p>
<p>其实安卓中也有类似的限制,.NET的NativeAOT之所以能在安卓平台下运行是因为.NET中对安卓进行了特殊处理,而在鸿蒙平台我们使用的是Linux平台的代码,所以没有对这些系统调用进行处理。</p>
<p>解决办法则是自行修改代码,将numa的函数全部修改为空函数。</p>
<p class="maodian"><a name="_lab2_4_1"></a></p><h3>5.2 mmap申请虚拟内存过大(已解决)</h3>
<p>解决上个问题后,.NET运行时初始化依然不能成功,导致程序崩溃,经过排查发现是GC初始化时会申请256G左右的虚拟内存,导致mmap返回Out Of Memory错误。</p>
<p>解决办法1:设置环境变量&ldquo;DOTNET_GCHeapHardLimit&rdquo;,将虚拟内存申请控制在约180G以下即可。</p>
<p>解决办法2:修改源代码,将USE_REGIONS宏关掉。</p>
<p class="maodian"><a name="_lab2_4_2"></a></p><h3>5.3 ICU,OpenSSL等第三方库缺失(已解决)</h3>
<p><strong>解决方案1</strong>:从Alpine上偷包 ,因为Alpine的libc是musl,所以理论上Alpine的库在鸿蒙上大部分都能使用。</p>
<blockquote><p>阿里云Alpine软件包镜像地址:<br />arm64架构:<a href="https://mirrors.aliyun.com/alpine/edge/main/aarch64/" rel="external nofollow" target="_blank">https://mirrors.aliyun.com/alpine/edge/main/aarch64/</a>amd64架构:<a href="https://mirrors.aliyun.com/alpine/edge/main/x86_64/" rel="external nofollow" target="_blank">https://mirrors.aliyun.com/alpine/edge/main/x86_64/</a></p></blockquote>
<p><strong>解决方案2</strong>:如果该库有cmake项目,则可以通过鸿蒙的CMake工具链编译。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202503/202503010851121.png" /></p>
<p class="maodian"><a name="_lab2_4_3"></a></p><h3>5.4 ICU初始化失败(已解决)</h3>
<p>鸿蒙的ICU配置文件路径与默认路径不同,需要调用修改环境变量API,将ICU_DATA修改为/system/usr/ohos_icu<br />且鸿蒙平台上libICU的大版本是72,要使用这个版本的库。</p>
<p class="maodian"><a name="_lab2_4_4"></a></p><h3>5.5 NativeAOT如何跨平台编译 (Windows平台已解决)</h3>
<p>NativeAOT众所周知不支持跨平台编译,而我的方案需要发布到linux-musl平台,所以无法在Windows上发布,影响开发效率。</p>
<p><strong>解决方案</strong>:在项目中引入项目<a href="https://github.com/OpenHarmony-NET/PublishAotCross" rel="external nofollow" target="_blank">https://github.com/OpenHarmony-NET/PublishAotCross</a></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202503/202503010851122.png" /></p>
<p class="maodian"><a name="_lab2_4_5"></a></p><h3>5.6 无法调用<code>Marshal.GetDelegateForFunctionPointer</code>相关函数</h3>
<p>Marshal.GetDelegateForFunctionPointer的实现依赖动态生成汇编,而HarmonyOS不支持动态生成汇编代码执行(Jit),使用该函数会导致崩溃。</p>
<p><strong>解决方案</strong>: 使用函数指针直接调用。</p>
<p class="maodian"><a name="_label5"></a></p><h2>6. 如何修改NativeAOT代码</h2>
<p>前文中提到部分问题的解决方案是修改源码,具体操作步骤如下:<br />修改完代码,执行以下命令进行编译(linux平台下,需要有编译环境):</p>
<div class="jb51code"><pre class="brush:vb;">
</pre></div>
<table><tbody><tr><td></td><td><code>./build.sh --subset clr.aot --configuration Release -arch arm64 --cross</code></td></tr></tbody></table>
<p>编译成功后,打开目录&nbsp;<code>运行时/artifacts/bin/coreclr/linux.arm64.Release/aotsdk</code>,将这里所有的替换到自己电脑nuget的缓存目录, 例如<code>C:\Users\用户名\.nuget\packages\runtime.linux-musl-arm64.microsoft.dotnet.ilcompiler\dotnet版本\sdk</code></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202503/202503010851123.png" /></p>
<p class="maodian"><a name="_label6"></a></p><h2>7.相关链接</h2>
<p><a href="https://github.com/dotnet/runtime/issues/110074" rel="external nofollow" target="_blank">https://github.com/dotnet/runtime/issues/110074</a></p>
<p><a href="https://github.com/dotnet/runtime/issues/111649" rel="external nofollow" target="_blank">https://github.com/dotnet/runtime/issues/111649</a></p>
頁: [1]
查看完整版本: .NET适配HarmonyOS进展的方法