春节假期不能出门,今天我们来玩儿一下 Rust 的 wasm target。
安装 target
看一下当前安装的 Rust 版本
$ rustc -V
rustc 1.40.0 (73528e339 2019-12-16)
执行
rustup target list
可以看到如下结果(列表很长,已把wasm相关的筛选出来),这几个是 Rust 支持的 wasm 相关的编译目标。
asmjs-unknown-emscripten
wasm32-unknown-emscripten
wasm32-unknown-unknown
wasm32-wasi
前 3 个,可以理解成编译出来的文件,用于加载到浏览器 web 页面(或有类似功能的运行时中)运行,它没有IO功能,因此常用于一些纯计算型任务(比如,把js中的计算密集型工作放wasm中来做)。具体可参考 https://rustwasm.github.io/book/introduction.html。
这里我们来玩 wasi。
准备一个简单的示例代码
新建一个项目
cargo new --bin testwasi
在 src/main.rs 中,加入下面这片代码,
use std::fs::File;
use std::io::prelude::*;
fn main() -> std::io::Result<()> {
let mut file = File::create("foo.txt")?;
file.write_all(b"Hello, world!")?;
println!("Hello, world!");
Ok(())
}
代码就是基本的终端打印和生成一个文本文件,就不多做解释了。
尝试 wasi
WASI 是 The WebAssembly System Interface 的缩写,简单来说就是一套接口标准,将 wasm 的应用领域从 web 中拓展到更广阔的各个平台中去(可以想像成类似 libc 之类的东西,但是是跨平台的)。
想了解更多的去 https://wasi.dev 这里,补一补。
使用前,需要添加 wasi target 基础包
$ rustup target add wasm32-wasi
info: downloading component 'rust-std' for 'wasm32-wasi'
会自动下载 rust-std 的 wasm32-wasi 目标版本。添加好后,执行
cargo build --target=wasm32-wasi
将工程编译到 wasi 目标。
看一下编译生成了什么文件。
$ ls target/
debug wasm32-wasi
可以看到,生成了一个 wasm32-wasi 的目录。继续看一下里面:
$ ls target/wasm32-wasi/
debug
$ ls target/wasm32-wasi/debug/
build deps examples incremental testwasi.d testwasi.wasm
好,我们已经生成了一个 testwasi.wasm 文件。看一下这个文件的属性:
$ ls target/wasm32-wasi/debug/testwasi.wasm -lh
-rwxrwxr-x 2 mike mike 1.9M 1月 30 18:25 target/wasm32-wasi/debug/testwasi.wasm
可执行文件?那么来执行一下:
$ ./target/wasm32-wasi/debug/testwasi.wasm
bash: ./target/wasm32-wasi/debug/testwasi.wasm: cannot execute binary file: Exec format error
出错了。犯愁,这个东东,咋运行呢?
运行时(Runtime)
运行这个文件,需要一个运行时(Runtime)。你也可以把这个理解成一个“容器”,“虚拟机”什么的,都行。但是准确的叫法是:运行时。
目前常见的运行时有 wasmtime,wasmer 等。
我们这次用 wasmtime 来运行吧。
下载 https://github.com/bytecodealliance/wasmtime/releases/download/v0.8.0/wasmtime-v0.8.0-x86_64-linux.tar.xz 或 https://github.com/bytecodealliance/wasmtime/releases/download/v0.8.0/wasmtime-v0.8.0-x86_64-linux-c-api.tar.xz (这两个有啥区别,从字面来看后者是遵从c-api规范的)。
解压安装(具体安装方法都懂吧)。
运行
$ wasmtime --dir=. target/wasm32-wasi/debug/testwasi.wasm
Hello, world!
$ ls
Cargo.lock Cargo.toml foo.txt src target
$ cat foo.txt
Hello, world!
可以看到,在终端下打出了 "Hello, world!",并在当前目录下,生成了 foo.txt 文件。
好吧,绕了一圈,跟直接 cargo run
运行的结果一模一样,感觉有点多此一举。而且性能肯定还不如 native 编译的高。这样做有啥用呢?
别忘了,.wasm 作为一个字节码文件,可以随便分发的。现在我是在 Ubuntu 18.04 64位平台编译的,这个 .wasm 文件,我可以扔到 Windows 10 上去,扔到 MacOS 上去,扔到 Ubuntu 16.04 64位平台去,扔到 Ubuntu 16.04 32 位平台去,扔到树莓派上去,扔到龙芯上去,等等,都可以运行。不过,前提是这些平台上已经安装了 wasmtime 这个 runtime。
更多话题
其实还有更多话题可以深入研究。比如:
-
wasm 文件的体积大小,如何优化,裁剪?
-
wasm 与原生可执行程序的性能差距,如何优化?
-
wasmtime 等 runtime 在各个平台上的支持是否完整?
Wasm 的更多玩法
wasm 作为一种字节码标准格式,有很多玩法。比如,
-
使用 wasm 可以实现 Rust 应用的插件系统,具有热加载的能力,可以替换脚本语言功能;
-
Rust 编译的库文件,可以编译成 wasm 文件,在其它语言中,利用对应的 wasm runtime 适配层,加载这个 wasm 库文件,调用里面的函数;
-
2中所说的模式,理论上可以扩展到任意语言间的交互。这样,wasm 俨然成为一种新的 C ABI;
-
很多应用形式,可以完全搬到 web 上运行;
-
wasm 作为一种新的应用发布格式,定义一种 zip 包格式,里面的核心文件是 wasm;
-
wasm 对 docker 的冲击?
结语
Wasm 是一个大的生态,而 Rust 与 wasm 是强相关,理由如下:
-
由于性能和体积大小的要求,Rust 比其它高级语言(特别是带 gc 的语言)更适合 wasm 开发,目前能够与 Rust 在这点上竞争的,只有 C/C++;
-
相对于 C/C++,Rust 更容易编写,写起来更安全,学习难度更低(C语言作为第一门语言,也挺难的,能精通指针的,不多);
-
目前 Rust 针对 wasm 的工具链,是最完整的,没有之一。
好了,我们在本文小试了一把牛刀,算了刚刚揭开了一厘米 wasm 的面纱。wasm 美不美,还得大家自己去摸索啦。
附录
mike@mike-Pocket:~$ rustup target list
aarch64-apple-ios
aarch64-fuchsia
aarch64-linux-android
aarch64-pc-windows-msvc
aarch64-unknown-linux-gnu
aarch64-unknown-linux-musl
arm-linux-androideabi
arm-unknown-linux-gnueabi
arm-unknown-linux-gnueabihf
arm-unknown-linux-musleabi
arm-unknown-linux-musleabihf
armebv7r-none-eabi
armebv7r-none-eabihf
armv5te-unknown-linux-gnueabi
armv5te-unknown-linux-musleabi
armv7-apple-ios
armv7-linux-androideabi
armv7-unknown-linux-gnueabi
armv7-unknown-linux-gnueabihf
armv7-unknown-linux-musleabi
armv7-unknown-linux-musleabihf
armv7r-none-eabi
armv7r-none-eabihf
armv7s-apple-ios
asmjs-unknown-emscripten
i386-apple-ios
i586-pc-windows-msvc
i586-unknown-linux-gnu
i586-unknown-linux-musl
i686-apple-darwin
i686-linux-android
i686-pc-windows-gnu
i686-pc-windows-msvc
i686-unknown-freebsd
i686-unknown-linux-gnu
i686-unknown-linux-musl
mips-unknown-linux-gnu
mips-unknown-linux-musl
mips64-unknown-linux-gnuabi64
mips64-unknown-linux-muslabi64
mips64el-unknown-linux-gnuabi64
mips64el-unknown-linux-muslabi64
mipsel-unknown-linux-gnu
mipsel-unknown-linux-musl
nvptx64-nvidia-cuda
powerpc-unknown-linux-gnu
powerpc64-unknown-linux-gnu
powerpc64le-unknown-linux-gnu
riscv32i-unknown-none-elf
riscv32imac-unknown-none-elf
riscv32imc-unknown-none-elf
riscv64gc-unknown-none-elf
riscv64imac-unknown-none-elf
s390x-unknown-linux-gnu
sparc64-unknown-linux-gnu
sparcv9-sun-solaris
thumbv6m-none-eabi
thumbv7em-none-eabi
thumbv7em-none-eabihf
thumbv7m-none-eabi
thumbv7neon-linux-androideabi
thumbv7neon-unknown-linux-gnueabihf
thumbv8m.base-none-eabi
thumbv8m.main-none-eabi
thumbv8m.main-none-eabihf
wasm32-unknown-emscripten
wasm32-unknown-unknown
wasm32-wasi
x86_64-apple-darwin
x86_64-apple-ios
x86_64-fortanix-unknown-sgx
x86_64-fuchsia
x86_64-linux-android
x86_64-pc-windows-gnu
x86_64-pc-windows-msvc
x86_64-rumprun-netbsd
x86_64-sun-solaris
x86_64-unknown-cloudabi
x86_64-unknown-freebsd
x86_64-unknown-linux-gnu (installed)
x86_64-unknown-linux-gnux32
x86_64-unknown-linux-musl
x86_64-unknown-netbsd
x86_64-unknown-redox
文章评论