在 OpenHarmony 開發(fā)板上運(yùn)行 WasmEdge
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
Why
移動(dòng)與 IoT 設(shè)備的特點(diǎn)是資源受限,軟硬件不統(tǒng)一,用戶體驗(yàn)卻要求很高。設(shè)備要能安全,跨平臺(tái)地運(yùn)行第三方開發(fā)者提供的軟件應(yīng)用(例如,應(yīng)用商店),因而直接原生編譯的軟件應(yīng)用(Native Client, or NaCl)并不主流。鴻蒙與安卓這樣的主流操作系統(tǒng)一般提供基于 Java 或者 JavaScript 的軟件執(zhí)行沙盒,來支持第三方應(yīng)用。但是這樣的軟件執(zhí)行沙盒有幾個(gè)大問題:
- 支持的編程語言很有限
- 支持的語言有 IP 與法律風(fēng)險(xiǎn)
- 性能一般
- 資源開銷大
- 無法支持實(shí)時(shí)系統(tǒng)
- 安全性一般(所以應(yīng)用商店需要審查制)
而 WebAssembly 作為一個(gè)多語言,跨平臺(tái),高性能,輕量級,安全的軟件執(zhí)行環(huán)境,能讓開發(fā)者兼得性能,可移植性,與安全性。WebAssembly 是移動(dòng)與 IoT 設(shè)備上系統(tǒng)中間件的最佳選擇。
WasmEdge 是由 CNCF 托管的輕量級、高性能和可擴(kuò)展的 WebAssembly runtime,適用于云原生、邊緣和去中心化應(yīng)用程序。WasmEdge 可以運(yùn)行 C/C++、Rust、Swift、AssemblyScript 或 Kotlin 等語言編譯的標(biāo)準(zhǔn) WebAssembly 字節(jié)碼程序。
OpenHarmony 是由開放原子開源基金會(huì)(OpenAtom Foundation)孵化及運(yùn)營的開源項(xiàng)目,目標(biāo)是面向全場景、全連接、全智能時(shí)代,基于開源的方式,搭建一個(gè)智能終端設(shè)備操作系統(tǒng)的框架和平臺(tái),促進(jìn)萬物互聯(lián)產(chǎn)業(yè)的繁榮發(fā)展。
WasmEdge 為 OpenHarmony 提供了一個(gè)與 JVM 與 JS engine 同級的 runtime,但是比 JVM、JS engine 更安全、更快、更小、更易于管理。通過 WasmEdge,可以在設(shè)備上安全地運(yùn)行第三方開發(fā)者用 C、C++、Rust 等語言編寫的 Wasm 程序,擴(kuò)大 OpenHarmony 的開發(fā)者群體。 WasmEdge 相當(dāng)于 OpenHarmony 的一個(gè)完全開源的開發(fā)執(zhí)行環(huán)境。社區(qū)開發(fā)者可以方便地運(yùn)行編譯好的 WebAssembly 程序,降低門檻。
WasmEdge 目前已經(jīng)支持了 Linux、macOS、Windows 與 實(shí)時(shí)操作系統(tǒng) seL4。添加 OpenHarmony 的支持,將豐富 WasmEdge 的生態(tài)。
介紹完畢 ,下面進(jìn)入編程時(shí)間。請參考下面的教程從源碼在 OpenHarmony 開發(fā)板中構(gòu)建和測試 WasmEdge。
1. 全量編譯 OpenHarmony OS
2. 獲取 WasmEdge 源碼
3. 修改 OpenHarmony 標(biāo)準(zhǔn)系統(tǒng)配置文件
4. 構(gòu)建 WasmEdge 與 OpenHarmony
5. 燒錄到開發(fā)板
6. 運(yùn)行 WasmEdge 提供的測試用例
配合視頻觀看,效果更佳。
環(huán)境準(zhǔn)備
OpenHarmony 標(biāo)準(zhǔn)系統(tǒng)
OpenHarmony 標(biāo)準(zhǔn)系統(tǒng)為開發(fā)者提供的 Docker 環(huán)境封裝了對應(yīng)的編譯工具鏈,本文檔主要介紹在 Docker 環(huán)境下構(gòu)建 WasmEdge 的步驟。
OpenHarmony 源碼的獲取與編譯可以參考 Open Harmony 提供的文檔 搭建Ubuntu環(huán)境-Docker方式。
請注意,在構(gòu)建 WasmEdge 前需要將 Openharmony 進(jìn)行一次全量編譯以便后續(xù) WasmEdge 的交叉編譯過程。
- # 獲取到 docker 鏡像后
- $ docker run -it -v $(pwd):/home/openharmony openharmony-docker-standard:0.0.5
- $ ./build.sh --product-name Hi3516DV300
獲取 WasmEdge 源碼
OpenHarmony 將第三方庫項(xiàng)目放在了 third_party 文件夾下,因此我們需要在 third_party 文件夾下獲取 WasmEdge 源碼。
這之后,用戶可以根據(jù)需要更改路徑并修改相關(guān)配置文件中的路徑。
- $ docker run -it -v $(pwd):/home/openharmony openharmony-docker-standard:0.0.5
- $ cd third_party
- $ git clone https://github.com/WasmEdge/WasmEdge.git
- $ cd WasmEdge
修改 OpenHarmony 標(biāo)準(zhǔn)系統(tǒng)配置文件
添加 WasmEdge 子系統(tǒng)配置
修改 OpenHarmony 的 build 目錄下的 subsystem_config.json 文件,如下添加 wasmedge 子系統(tǒng)。
- {
- ...
- "wasmedge": {
- "path": "third_party/WasmEdge",
- "name": "wasmedge"
- },
- ...
- }
將組件添加到產(chǎn)品配置中
修改 OpenHarmony 產(chǎn)品配置文件,標(biāo)準(zhǔn)系統(tǒng)對應(yīng)的配置文件為:productdefine/common/products/Hi3516DV300.json。
在該配置文件中添加 "wasmedge:wasmedge":{},表示該產(chǎn)品會(huì)編譯并打包 wasmedge 子系統(tǒng)下的 wasmedge 模塊到版本中。
- {
- ...
- "parts":{
- ...
- "wasmedge:wasmedge":{}
- }
- }
構(gòu)建 WasmEdge 與 OpenHarmony
說明
在 OpenHarmony 中構(gòu)建的 WasmEdge 目前僅支持 wasmedge,即 wasm 的通用運(yùn)行時(shí)。
wasmedge 可以在解釋器模式下執(zhí)行一個(gè) WASM 文件, 也可以執(zhí)行從 WASM 文件 AOT 預(yù)編譯產(chǎn)生的機(jī)器碼二進(jìn)制格式文件。但目前還不支持在 OpenHarmony 中對 WASM 文件進(jìn)行 AOT 預(yù)編譯 。
執(zhí)行構(gòu)建腳本
執(zhí)行 WasmEdge 源碼下的 utils/build_for_ohos.sh 命令行腳本,將自動(dòng)執(zhí)行以下工作:
將 .gn 等 OpenHarmony 需要的構(gòu)建配置文件移動(dòng)到 WasmEdge 項(xiàng)目根目錄;
使用 OpenHarmony 的編譯工具鏈進(jìn)行交叉編譯構(gòu)建 WasmEdge;
運(yùn)行 OpenHarmony 的構(gòu)建腳本 build.sh 進(jìn)行全量編譯,該步驟將 wasmedge 添加進(jìn) OpenHarmony OS;
- $ docker run -it -v $(pwd):/home/openharmony openharmony-docker-standard:0.0.5
- $ cd third_party/WasmEdge/utils/ohos
- $ ./build_for_ohos.sh /home/openharmony
當(dāng) terminal 顯示以下信息時(shí),表明編譯完成。
- ...
- post_process
- =====build Hi3516DV300 successful.
- 2021-12-15 03:18:50
- ++++++++++++++++++++++++++++++++++++++++
檢查 wasmedge 是否編譯打包進(jìn) OpenHarmony OS。
- $ cd /home/openharmony/out/ohos-arm-release/packages/phone/system/bin
- $ ls
當(dāng)輸出的文件名中存在 wasmedge 時(shí),就表明 WasmEdge 已經(jīng)成功導(dǎo)入到 OpenHarmony OS。
測試
燒錄鏡像
將重新編譯后的 OpenHarmony 標(biāo)準(zhǔn)系統(tǒng)鏡像燒錄進(jìn)開發(fā)板,具體見 OpenHarmony 提供的文檔 Hi3516DV300 開發(fā)板燒錄。
運(yùn)行應(yīng)用
WasmEdge 在 tools/wasmedge/examples/ 文件夾提供了測試樣例。在 OpenHarmony 標(biāo)準(zhǔn)系統(tǒng)中,這些樣例寫入了 system 鏡像中,依然可以進(jìn)行測試。通過串口工具連接上開發(fā)板并啟動(dòng)OpenHarmony 標(biāo)準(zhǔn)系統(tǒng)后,我們就可以進(jìn)行以下的測試。
- # cd /system/usr/wasmedge_example
- # wasmedge hello.wasm 1 2 3
- hello
- 1
- 2
- 3
- # wasmedge --reactor add.wasm add 2 2
- 4
- # wasmedge --reactor fibonacci.wasm fib 8
- 34
- # wasmedge --reactor factorial.wasm fac 12
- 479001600
- #
- # cd js
- # wasmedge --dir .:. qjs.wasm hello.js 1 2 3
- Hello 1 2 3
下一步
接下來,你可以參考 WasmEdge Book 在 OpenHarmony 標(biāo)準(zhǔn)系統(tǒng)中使用 WasmEdge Runtime 來運(yùn)行你自己的 WebAssembly 應(yīng)用。
移植過程踩過的坑
最后和大家分享一下,在移植 WasmEdge 到 OpenHarmony OS 過程出現(xiàn)的一些問題與值得注意的地方。
交叉編譯
cmake 項(xiàng)目進(jìn)行交叉編譯需要配置工具鏈,官方的交叉編譯配置給出了參考,但需要在此基礎(chǔ)上細(xì)化,如指明 Clang 及 Clang++ 的位置。此外,標(biāo)準(zhǔn)版 sysroot 的路徑也有所不同,具體可以參考 WasmEdge 中的配置:
- set(TOOLSCHAIN_PATH "${OHOS_DIR_PATH}/prebuilts/clang/ohos/linux-x86_64/llvm")
- set(TOOLCHAIN_HOST "${TOOLSCHAIN_PATH}/bin")
- set(OHOS_SYSROOT_PATH "${OHOS_DIR_PATH}/out/ohos-arm-release/obj/third_party/musl")
- set(CMAKE_SYSROOT ${OHOS_SYSROOT_PATH})
- set(CMAKE_CROSSCOMPILING TRUE)
- set(CMAKE_SYSTEM_NAME "Generic")
- set(CMAKE_CXX_COMPILER_ID Clang)
- set(CMAKE_TOOLCHAIN_PREFIX llvm-)
- set(LLVM_PATH "${OHOS_DIR_PATH}/prebuilts/clang/ohos/linux-x86_64/llvm")
- include_directories(${LLVM_PATH}/include/c++/v1)
- include_directories(${OHOS_SYSROOT_PATH}/usr/include/arm-linux-ohosmusl)
- link_directories(${OHOS_SYSROOT_PATH}/usr/lib/arm-linux-ohosmusl)
- set(TOOLCHAIN_CC "${TOOLCHAIN_HOST}/clang")
- set(TOOLCHAIN_CXX "${TOOLCHAIN_HOST}/clang++")
- set(CMAKE_C_COMPILER ${TOOLCHAIN_CC})
- set(CMAKE_C_FLAGS "--target=arm-linux-ohosmusl -D__clang__ -march=armv7-a -mfloat-abi=softfp -mtune=generic-armv7-a -mfpu=neon -mthumb -w --sysroot=${OHOS_SYSROOT_PATH}")
- set(CMAKE_CXX_COMPILER ${TOOLCHAIN_CXX})
- set(CMAKE_CXX_FLAGS "--target=arm-linux-ohosmusl -D__clang__ -march=armv7-a -mfloat-abi=softfp -mtune=generic-armv7-a -mfpu=neon -mthumb -w --sysroot=${OHOS_SYSROOT_PATH}")
- set(MY_LINK_FLAGS "--target=arm-linux-ohosmusl --sysroot=${OHOS_SYSROOT_PATH}")
- set(CMAKE_LINKER clang)
- set(CMAKE_CXX_LINKER clang++)
- set(CMAKE_C_LINKER clang)
- set(CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINKER} ${MY_LINK_FLAGS} <FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
- set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINKER} ${MY_LINK_FLAGS} <FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
編譯工具鏈
OpenHarmony OS 使用 gn+ninja 進(jìn)行編譯。對于 cmake 組織編譯的項(xiàng)目來說,官方提供的 cmake 項(xiàng)目移植文檔給出的案例是基于輕量級系統(tǒng)的,并不完全適用于標(biāo)準(zhǔn)版系統(tǒng)上的移植。要想將項(xiàng)目寫入 OpenHarmony OS 編譯生成的鏡像燒錄到開發(fā)板上,需要編寫 gn 腳本參與進(jìn) OpenHarmony OS 的編譯過程。
在 WasmEdge 編譯過程中,需要使用到 spqlog 項(xiàng)目,構(gòu)建過程中存在 spdlog 項(xiàng)目拉取及編譯生成 sqdlog 靜態(tài)庫的動(dòng)作,這意味著只將 WasmEdge 相關(guān)庫的編譯過程改寫為 gn 腳本是不夠的,還需要對 spdlog 的編譯過程進(jìn)行改寫,使得工作量急劇增加。
那么對于項(xiàng)目所依賴但并不屬于 OpenHarmony OS 中的靜態(tài)庫模塊,編譯過程中要如何將這一模塊引入 OpenHarmony OS 呢?
OpenHarmony OS 提供的 gn 編寫模板中有 ohos_copy ,它可以將生成的靜態(tài)庫移至生成的目標(biāo)文件夾。這樣在真正編譯需要鏈接時(shí),就能將這一靜態(tài)庫視為 OpenHarmony OS 的原生模塊而不是查無此庫。在 WasmEdge 的移植過程中,所執(zhí)行的編譯腳本便是事先進(jìn)行一遍交叉編譯,生成需要 copy 的 spdlog 靜態(tài)庫,然后再執(zhí)行 OpenHarmony OS 的編譯腳本,從而按照項(xiàng)目目錄下的 BUILD.gn 內(nèi)的定義組織編譯。
在 WasmEdge 的 BUILD.gn 中,關(guān)于 spdlog 靜態(tài)庫的描述如下:
- ohos_copy("spdlog"){
- sources = [
- "$WASMEDGE_ROOT_DIR/build/_deps/spdlog-build/libspdlog.a",
- ]
- outputs = [
- target_out_dir + "/lib/libspdlog.a"
- ]
- module_install_name = ""
- }
標(biāo)準(zhǔn) C 庫
平時(shí)我們常用的標(biāo)準(zhǔn) C 庫是 GNU 發(fā)布的 libc 庫,而 OpenHarmony 中使用的是 Musl-libc,因此如果需要移植的項(xiàng)目代碼中使用了 glibc 的宏變量的代碼,那么需要進(jìn)行修改或者在開頭重新定義為 Musl-libc 中的宏變量。
鏈接項(xiàng)
- ../../third_party/WasmEdge/lib/system/allocator.cpp:64:40: error: unused variable 'k4G' [-Werror,-Wunused-const-variable]
- static inline constexpr const uint64_t k4G = UINT64_C(0x100000000);
- ^
- ../../third_party/WasmEdge/lib/system/allocator.cpp:65:40: error: unused variable 'k12G' [-Werror,-Wunused-const-variable]
- static inline constexpr const uint64_t k12G = UINT64_C(0x300000000);
- ^
- 1 warning and 2 errors generated.
諸如這類報(bào)錯(cuò),在 BUILD.gn 中使用到該源碼的模塊中添加 cflags.例如,對上面的報(bào)錯(cuò),可以添加如下的 cflags :
- cflags = [
- ...
- "-Wno-unused-const-variable",
- ...
- ]
如果出現(xiàn)下面的 C++ 的鏈接編譯報(bào)錯(cuò),
- ../../third_party/WasmEdge/lib/host/wasi/inode-linux.cpp:745:3: error: cannot use 'try' with exceptions disabled
- try {
- ^
則添加 cflags_cc:
- # BUILD.gn 使用到該源碼的相應(yīng)模塊
- {
- ...
- cflags_cc = [
- ...
- "-fexceptions",
- ...
- ]
- }
移植過程中還有許多諸如此類的編譯報(bào)錯(cuò),在此不進(jìn)行一一列舉。