从 JavaScript 调用本地函数 | 用 WasmEdge 运行JavaScript 程序
WasmEdge 让 JavaScript 可以在共享库调用本地函数。
在前三篇文章中,我解释了为什么以及如何在 WebAssembly 沙箱中运行 JavaScript 程序。同时,还讨论了如何使用 Rust 为 WasmEdge 创建自定义 JavaScript AP。
- 用 WasmEdge 在 WebAssembly 中运行 JavaScript
- 将 JavaScript 嵌入到 Rust 中,并在 WebAssembly 中运行
- 用 Rust 创建高性能 JavaScript API,并在 WebAssembly 中运行
但是,为了完全访问底层系统的操作系统和硬件功能,我们有时需要为基于 C 的本机函数创建 JavaScript API。 也就是说,当 JavaScript 程序调用预定义的函数时,WasmEdge 会将其传递给 OS 上的原生共享库执行。
本文中,我们将向你展示如何做到这一点。我们将创建以下两个组件。
- 一个定制的 WasmEdge runtime,允许 WebAssembly 函数调用外部原生函数。
- 一个定制的 QuickJS 解释器,用于解析 JavaScript 中的函数调用,并将外部函数调用传递给 WebAssembly,后者又将它们传递给原生函数调用。 为了能够 follow 这个例子,你需要 fork 或克隆 wasmedge-quickjs repo。示例在该Repo的
examples/host_function
文件夹。
$ git clone https://github.com/second-state/wasmedge-quickjs/
将一个基于 C 的函数嵌入 WasmEdge
首先,我们将向 WasmEdge runtime 添加一个基于 C 的函数,以便我们的 JavaScript 程序可以稍后调用它。我们使用 WasmEdge C API 创建一个 HostInc
函数,然后将其注册为 host_inc
。
wasmedge_c/demo_wasmedge.c
文件包含 host 函数的完整源代码和其在 WasmEdge 的注册。
#include <stdio.h> #include "wasmedge.h" WasmEdge_Result HostInc(void *Data, WasmEdge_MemoryInstanceContext *MemCxt, const WasmEdge_Value *In, WasmEdge_Value *Out) { int32_t Val1 = WasmEdge_ValueGetI32(In[0]); printf("Runtime(c)=> host_inc call : %d\n",Val1 + 1); Out[0] = WasmEdge_ValueGenI32(Val1 + 1); return WasmEdge_Result_Success; } // mapping dirs char* dirs = ".:..\0"; int main(int Argc, const char* Argv[]) { /* Create the configure context and add the WASI support. */ /* This step is not necessary unless you need WASI support. */ WasmEdge_ConfigureContext *ConfCxt = WasmEdge_ConfigureCreate(); WasmEdge_ConfigureAddHostRegistration(ConfCxt, WasmEdge_HostRegistration_Wasi); /* The configure and store context to the VM creation can be NULL. */ WasmEdge_VMContext *VMCxt = WasmEdge_VMCreate(ConfCxt, NULL); WasmEdge_ImportObjectContext *WasiObject = WasmEdge_VMGetImportModuleContext(VMCxt, WasmEdge_HostRegistration_Wasi); WasmEdge_ImportObjectInitWASI(WasiObject,Argv+1,Argc-1,NULL,0,&dirs,1,NULL,0); /* Create the import object. */ WasmEdge_String ExportName = WasmEdge_StringCreateByCString("extern"); WasmEdge_ImportObjectContext *ImpObj = WasmEdge_ImportObjectCreate(ExportName, NULL); enum WasmEdge_ValType ParamList[1] = { WasmEdge_ValType_I32 }; enum WasmEdge_ValType ReturnList[1] = { WasmEdge_ValType_I32 }; WasmEdge_FunctionTypeContext *HostFType = WasmEdge_FunctionTypeCreate(ParamList, 1, ReturnList, 1); WasmEdge_HostFunctionContext *HostFunc = WasmEdge_HostFunctionCreate(HostFType, HostInc, 0); WasmEdge_FunctionTypeDelete(HostFType); WasmEdge_String HostFuncName = WasmEdge_StringCreateByCString("host_inc"); WasmEdge_ImportObjectAddHostFunction(ImpObj, HostFuncName, HostFunc); WasmEdge_StringDelete(HostFuncName); WasmEdge_VMRegisterModuleFromImport(VMCxt, ImpObj); /* The parameters and returns arrays. */ WasmEdge_Value Params[0]; WasmEdge_Value Returns[0]; /* Function name. */ WasmEdge_String FuncName = WasmEdge_StringCreateByCString("_start"); /* Run the WASM function from file. */ WasmEdge_Result Res = WasmEdge_VMRunWasmFromFile(VMCxt, Argv[1], FuncName, Params, 0, Returns, 0); if (WasmEdge_ResultOK(Res)) { printf("\nRuntime(c)=> OK\n"); } else { printf("\nRuntime(c)=> Error message: %s\n", WasmEdge_ResultGetMessage(Res)); } /* Resources deallocations. */ WasmEdge_VMDelete(VMCxt); WasmEdge_ConfigureDelete(ConfCxt); WasmEdge_StringDelete(FuncName); return 0; }
你可以使用一个标准的 C 编译器,如 GCC,来编译 C 源代码。
#build custom webassembly Runtime $ cd wasmedge_c #build a custom Runtime wasmedge_c/$ gcc demo_wasmedge.c -lwasmedge_c -o demo_wasmedge
编译器生成一个二进制码可执行文件 demo_wasmedge
,用于定制化的含有 host 函数的 WasmEdge runtime 版本。
创建一个 Rust 模块来将函数 bind 到 JavaScript
接下来,我们需要在 Rust 中创建一个定制的 JavaScript 解释器。它解释对 host_inc
的 JavaScript 调用,并通过自定义的 WasmEdge runtime (demo_wasmedge
) 将调用定向到本地 C 函数。src/main.rs 文件有完整的 Rust 源代码,用于注册外部函数。
mod host_extern { use quickjs_rs_wasi::{Context, JsValue}; #[link(wasm_import_module = "extern")] extern "C" { pub fn host_inc(v: i32) -> i32; } pub struct HostIncFn; impl quickjs_rs_wasi::JsFn for HostIncFn { fn call(ctx: &mut Context, _this_val: JsValue, argv: &[JsValue]) -> JsValue { if let Some(JsValue::Int(i)) = argv.get(0) { unsafe { let r = host_inc(*i); r.into() } } else { ctx.throw_type_error("'v' is not a int").into() } } } } use quickjs_rs_wasi::*; fn main() { let mut ctx = Context::new(); let f = ctx.new_function::<host_extern::HostIncFn>("host_inc"); ctx.get_global().set("host_inc", f.into()); // Run the embedded JavaScript ctx.eval_global_str("print('js=> host_inc(2)=',host_inc(2))"); }
Rust 程序创建一个定制的 QuickJS 解释器,然后执行一个 JavaScript 程序,该程序依次调用在 WasmEdge runtime 中注册的基于 C 的本地函数。
$ cargo build --target wasm32-wasi --release
带有嵌入的 JavaScript 程序的定制 QuickJS 解释器可以在 target/wasm32-wasi/release/quickjs-rs-wasi.wasm
查看。
调用 JavaScript 函数
嵌入的 JavaScript 程序调用 host_inc()
函数。 JavaScript 解释器(host_function.wasm
的 Rust 程序)将此调用路由到 WebAssembly host_inc()
调用。定制的 WasmEdge 运行时(demo_wasmedge
的 C 程序)将 WebAssembly 调用路由到本机 C 函数。
print('js=> host_inc(2)=',host_inc(2))
当然,你也可以编写一个从文件中读取 JavaScript 的通用 Rust 程序。
要运行此 JavaScript,你需要在我们定制的 WasmEdge Runtime 中使用我们定制的 QuickJS 解释器。解释器和运行时都经过检测以支持 host_inc
本机函数调用。
$ cd wasmedge_c $ export LD_LIBRARY_PATH=. $ ./demo_wasmedge --dir .:. ../target/wasm32-wasi/release/host_function.wasm js=> host_inc(2)= 3
接下来
上面这个简单的例子展示了如何将一个基于 C 的原生函数变为一个 JavaScript API。你可以使用同样的方式添加很多本地 API 到 JavaScript。非常期待你的绝妙点子!
云原生 WebAssembly 中的 JavaScript 是下一代云和边缘计算基础设施中的新兴领域。 我们也是刚刚起步!如果你也感兴趣,请加入我们的 WasmEdge 项目(或通过提出 feature request issue 告诉我们你的需求)。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Kubernetes CRI 分析 - kubelet 创建 Pod 分析
kubelet CRI 创建 Pod 调用流程 本文以 kubelet dockershim 创建 Pod 调用流程为例做分析。 kubelet 通过调用 dockershim 来创建并启动容器,而 dockershim 则调用 Docker 来创建并启动容器,并调用 CNI 来构建 Pod 网络。 kubelet dockershim 创建 Pod 调用流程图 dockershim 属于 kubelet 内置 CRI shim,其余的 remote CRI shim 创建 Pod 调用流程其实与 dockershim 调用基本一致,只不过是调用了不同的容器引擎来操作容器,但一样由 CRI shim 调用 CNI 来构建 Pod 网络。 下面是详细的源码分析。 kubeGenericRuntimeManager 的 SyncPod 方法,调用 CRI 创建 Pod 的逻辑将在该方法里触发。 从该方法代码也可以看出,kubelet 创建一个 Pod 的逻辑为: 先创建并启动 Pod sandbox 容器,并构建好 Pod 网络。 创建并启动 ephemeral containers。 创...
- 下一篇
分布式事务(一)—分布式事务的概念
现今互联网界,分布式系统和微服务架构盛行。一个简单操作,在服务端非常可能是由多个服务和数据库实例协同完成的。在互联网金融等一致性要求较高的场景下,多个独立操作之间的一致性问题显得格外棘手。随着业务的快速发展、业务复杂度越来越高,几乎每个公司的系统都会从单体走向分布式,特别是转向微服务架构,随之而来就必然遇到分布式事务这个难题。本文会介绍分布式事务的一些相关概念。 分布式事务的概念 数据库事务 数据库事务的目的 数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。数据库事务通常包含了一个序列的对数据库的读/写操作。包含有以下两个目的: 为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。 当事务被提交给了数据库管理系统(DBMS),则DBMS需要确保该事务中的所有操作都成功完成且其结果被永久保存在数据库中,如果事务中有的操作没有成功完成,则事务中的所有操作都需要回滚,回到事务执行前的状...
相关文章
文章评论
共有0条评论来说两句吧...