likes
comments
collection
share

golang硬核技术(三)用rust通过wasm调用go,1.21新版尝鲜

作者站长头像
站长
· 阅读数 21

前言

Wasm,即WebAssembly,是一种用来补充JS在运行上不足的“低级”语言——基于二进制编写。其目标之一正是达到在网页上如同运行机器语言一样快速高效。其开发团队分别来自Mozilla、Google、Microsoft、Apple。

Wasm允许用户采用自己熟悉的语言书写(目前支持C/C++/Rust),再在虚拟机引擎在浏览器上运行。它支持沙盒模式,即先用高级语言编写wasm模块,再在JS中以库函数加载。

WebAssembly,我们都知道,是一种新的字节码格式,目前被应用于 web 中,由于其可移植、体积小,安全性的等优点被渐渐广泛认可,但是其主要是运行在浏览器中。

一些天才们,想让 WebAssembly 也可以运行在非浏览器环境中,这就产生了 WASI。

WASI是一个新的API体系, 由Wasmtime项目设计, 目的是为WASM设计一套引擎无关(engine-indepent), 面向非Web系统(non-Web system-oriented)的API标准.


上面是我粘的,总结起来就是,将各种语言编写成wasm脚本,然后依托一个运行时运行这些脚本 基于这种特点,Wasm在智能合约,云原生,faas等领域非常火热

下面我们将go代码编写成wasm脚本,然后在rust构建的运行时中运行。

golang wasip1

在本月8号,golang发布了最新版本。我知道有小伙伴打不开,我这里放了截图。

golang硬核技术(三)用rust通过wasm调用go,1.21新版尝鲜

意思是说golang已经提供了wasi的preview1支持。并提供go:wasmimport标签用于引入函数。 至于go:wasmexport导出标签,目前还不支持。当然小伙伴们也不用着急,我翻了开发者日志,计划在2024年支持。

先写一段测试代码

//go:wasmimport env show
func show(buf unsafe.Pointer, len uint32) (errno uint32)

func main() {
   ctn := []byte("hello world")
   show(unsafe.Pointer(&ctn[0]), uint32(len(ctn)))
}

先用go:wasmimport标签引入函数,namespace为env,函数名为show。这个函数的作用是用来显示一段文本。 然后在main函数中调用show函数,显示一个hello world

运行下面的命令编译成wasm脚本

GOOS=wasip1 GOARCH=wasm go build -ldflags "-s -w" -o wasi-go.wasm main.go

wasmer

主流的wasm运行时有**wasmedge、wasmtime、WAVM wasmer

这里我们使用star较高的wasmer

代码如下

use std::mem::MaybeUninit;
use wasmer::{
    Extern, Function, FunctionEnv, FunctionEnvMut, Instance, Memory, Memory32, Module, Store,
    WasmPtr,
};
use wasmer_wasix::WasiEnv;

#[derive(Debug)]
pub struct WasiFunctionEnv {
    memory: MaybeUninit<Memory>,
}

impl Default for WasiFunctionEnv {
    fn default() -> Self {
        let memory = MaybeUninit::uninit();
        Self { memory }
    }
}

impl WasiFunctionEnv {
    pub fn show(
        mut ctx: FunctionEnvMut<'_, WasiFunctionEnv>,
        buf: WasmPtr<u8, Memory32>,
        len: i32,
    ) -> i32 {
        let (env, store) = ctx.data_and_store_mut();
        unsafe {
            let view = env.memory.assume_init_ref().view(&store);
            let s = buf.read_utf8_string(&view, len as u32).unwrap();
            println!("show---> {}", s);
        }
        1i32
    }
}

fn main() {
    //加载wasi文件,创建store module
    let wasm_bytes = include_bytes!("../wasi_go.wasm");
    let mut store = Store::default();
    let module = Module::new(&store, wasm_bytes).unwrap();
    // let (stdout_tx, mut stdout_rx) = Pipe::channel();

    //初始化tokio异步运行时
    let runtime = tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .unwrap();
    let _guard = runtime.enter();
    // Run the module.

    //初始化wasi函数环境
    let mut wasi_env = WasiEnv::builder("hello")
        // .args(&["world"])
        // .env("KEY", "Value")
        // .stdout(Box::new(stdout_tx))
        .finalize(&mut store)
        .unwrap();

    //创建一个外部函数,并注册到环境中
    let mut import_object = wasi_env.import_object(&mut store, &module).unwrap();
    let env = WasiFunctionEnv::default();
    let env = FunctionEnv::new(&mut store, env);
    let function_show = Extern::Function(Function::new_typed_with_env(
        &mut store,
        &env,
        WasiFunctionEnv::show,
    ));
    import_object.register_namespace("env", [("show".to_string(), function_show)]);

    //创建实例,并对齐内存
    let instance = Instance::new(&mut store, &module, &import_object).unwrap();
    wasi_env.initialize(&mut store, instance.clone()).unwrap();
    let memory = instance.exports.get_memory("memory").unwrap();
    env.as_mut(&mut store).memory.write(memory.clone());

    //调用go的main函数
    let start = instance.exports.get_function("_start").unwrap();
    let result = start.call(&mut store, &[]);
    println!("result---> {:?}", result);

    // let mut buf = String::new();
    // let _ = stdout_rx.read_to_string(&mut buf);
    wasi_env.cleanup(&mut store, None);
}

注意:

  • wasm运行时的内存和rust程序自身的内存是不一样的,所以在读取数据的时候要先对齐内存。
  • _start对应go的main函数。是go唯一导出的函数。所以会退出程序,打印result可以看到ExitCode::success(0)
  • 我这里通过function的方式调起的脚本,也可以通过WasiEnv.run_with_store像调用程序一样调起脚本。
  • Pipe可以监控脚本的输入输出

运行起来查看效果,正常打印了文本。

golang硬核技术(三)用rust通过wasm调用go,1.21新版尝鲜

尾语

不过wasm也有自身的优势,期待它的未来,

转载自:https://juejin.cn/post/7268684084336033849
评论
请登录