使用操作系统服务
WASI(WebAssembly 系统接口)标准被设计来让 WebAssembly 应用程序可以访问操作系统服务。 Rust 编译器中的 wasm32-wasi 目标支持 WASI。 在这一部分中,我们将使用一个示例工程来展示如何使用 Rust 的标准库来访问操作系统服务。
随机数
WebAssembly 虚拟机是一个纯软件实现,它不具有生成随机数必须的硬件资源。这就是 WASI 为 WebAssembly 定义了一个从宿主操作系统获取一个随机种子的函数的原因。作为一个 Rust 开发者,你需要做的只是使用受欢迎的 rand 或者 getrandom 包。得益于 wasm32-wasi 编译器后端,这些包将在 WebAssembly 字节码中生成正确的 WASI 调用。如下是 Cargo.toml 中的依赖部分。
[dependencies] rand = "0.7.3" getrandom = "0.1.14"
如下是在 WebAssembly 生成随机数的 Rust 代码。
2. #![allow(unused)]
3. fn main() {
4. use rand::prelude::*;
6. pub fn get_random_i32() -> i32 {
7. let x: i32 = random();
8. return x;
9. }
11. pub fn get_random_bytes() -> Vec<u8> {
12. let mut rng = thread_rng();
13. let mut arr = [0u8; 128];
14. rng.fill(&mut arr[..]);
15. return arr.to_vec();
16. }
17. }
从 Rust 中输出和调试
Rust 中的 println! 宏同样可以用在 WASI 中。下面的语句向运行 WasmEdge 的进程的 STDOUT 输出。
2. #![allow(unused)]
3. fn main() {
4. pub fn echo(content: &str) -> String {
5. println!("Printed from wasi: {}", content);
6. return content.to_string();
7. }
8. }
参数以及环境变量
在 WasmEdge 应用程序中,你可以传递命令行参数并且获取操作系统环境变量。 在 Rust 中你可以使用 env::args() 和 env::vars() 访问他们。
2. #![allow(unused)]
3. fn main() {
4. use std::env;
6. pub fn print_env() {
7. println!("The env vars are as follows.");
8. for (key, value) in env::vars() {
9. println!("{}: {}", key, value);
10. }
12. println!("The args are as follows.");
13. for argument in env::args() {
14. println!("{}", argument);
15. }
16. }
17. }
读写文件
WASI 让你可以通过标准的 Rust std::fs 接口来访问宿主的文件系统。 在 Rust 程序中,你通过相对路径来操作文件,相对路径的根目录可以在启动 WasmEdge Runtime 的时候被指定。
2. #![allow(unused)]
3. fn main() {
4. use std::fs;
5. use std::fs::File;
6. use std::io::{Write, Read};
8. pub fn create_file(path: &str, content: &str) {
9. let mut output = File::create(path).unwrap();
10. output.write_all(content.as_bytes()).unwrap();
11. }
13. pub fn read_file(path: &str) -> String {
14. let mut f = File::open(path).unwrap();
15. let mut s = String::new();
16. match f.read_to_string(&mut s) {
17. Ok(_) => s,
18. Err(e) => e.to_string(),
19. }
20. }
22. pub fn del_file(path: &str) {
23. fs::remove_file(path).expect("Unable to delete");
24. }
25. }
包含 main() 的应用程序
包含 main() 函数的 Rust 程序可以被编译为一个独立的 WebAssembly 程序。
1. fn main() {
2. println!("Random number: {}", get_random_i32());
3. println!("Random bytes: {:?}", get_random_bytes());
4. println!("{}", echo("This is from a main function"));
5. print_env();
6. create_file("tmp.txt", "This is in a file");
7. println!("File content is {}", read_file("tmp.txt"));
8. del_file("tmp.txt");
9. }
使用如下的命令来编译 Rust 工程。
cargo build --target wasm32-wasi
使用如下命令在 wasmedge 中运行它。--dir 选项将命令行当前的目录映射为 WebAssembly 程序中文件系统的当前目录。
$ wasmedge --dir .:. target/wasm32-wasi/debug/wasi.wasm hello Random number: -68634548 Random bytes: [87, 117, 194, 122, 74, 189, 29, 1, 113, 26, 90, 6, 151, 20, 11, 169, 131, 212, 161, 220, 216, 190, 77, 234, 30, 10, 159, 7, 14, 89, 81, 111, 247, 136, 39, 195, 83, 90, 153, 225, 66, 16, 150, 217, 137, 172, 216, 203, 251, 37, 4, 27, 32, 57, 76, 237, 99, 147, 24, 175, 208, 157, 3, 220, 46, 224, 199, 153, 144, 96, 120, 89, 160, 38, 171, 239, 87, 218, 41, 184, 220, 78, 157, 57, 229, 198, 222, 72, 219, 118, 237, 27, 229, 28, 51, 116, 88, 101, 40, 139, 160, 51, 156, 102, 66, 233, 101, 50, 131, 9, 253, 186, 73, 148, 85, 36, 155, 254, 168, 202, 23, 96, 181, 99, 120, 136, 28, 147] This is from a main function The env vars are as follows. ... ... The args are as follows. target/wasm32-wasi/debug/wasi.wasm hello File content is This is in a file
函数
正如我们之前看到的,你可以在 Rust lib.rs 中创建 WebAssembly 函数。在这些函数中,你同样可以使用 WASI 函数。然而,需要注意的是,没有 main() 函数的情况下,你将会需要显式地调用一个辅助函数来初始化环境,以此让 WASI 函数正常工作。 在 Cargo.toml 中添加一个辅助包,这样的话 WASI 初始化代码将会应用在你导出的的公开库函数上。
[dependencies] ... ... wasmedge-wasi-helper = "=0.2.0"
在访问任何参数和环境变量或者操作任何文件之前,我们需要调用 _initialize() 函数。
2. #![allow(unused)]
3. fn main() {
4. pub fn print_env() -> i32 {
5. _initialize();
6. ... ...
7. }
9. pub fn create_file(path: &str, content: &str) -> String {
10. _initialize();
11. ... ...
12. }
14. pub fn read_file(path: &str) -> String {
15. _initialize();
16. ... ...
17. }
19. pub fn del_file(path: &str) -> String {
20. _initialize();
21. ... ...
22. }
23. }
