Skip to content

Day 86: std.wasm模块:WebAssembly交互

WebAssembly(Wasm)是一种可移植的、低级的二进制指令格式,它允许开发者使用像C, C++, Rust, 和Zig这样的高性能语言来编写可以在Web浏览器和其它环境中(如Node.js, Deno, 或独立的Wasm运行时)安全、高速运行的代码。

Zig对WebAssembly提供了一流的支持。由于Zig没有庞大的运行时且能精细控制内存,它非常适合生成小巧而高效的Wasm模块。此外,Zig标准库也提供了用于解析Wasm模块的工具。

将Zig代码编译到Wasm非常简单,只需在构建时指定目标平台即可。

Terminal window
# 编译为独立的Wasm模块
zig build-exe my_app.zig -target wasm32-freestanding
# 在build.zig中设置目标
exe.target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });

-target wasm32-freestanding告诉编译器生成一个不依赖任何特定操作系统API的32位Wasm模块。

std.wasm模块提供了一个解析器,可以用来检查一个.wasm文件的结构。Wasm模块由多个“节”(Sections)组成,例如:

  • Type Section: 定义了模块中使用的所有函数签名。
  • Import Section: 声明了模块从宿主环境(如JavaScript)导入的函数。
  • Function Section: 将类型签名与模块内部的函数体关联起来。
  • Export Section: 声明了模块向宿主环境导出的函数、内存等。
  • Code Section: 包含了实际的Wasm字节码。
const std = @import("std");
const wasm = std.wasm;
pub fn main() !void {
const allocator = std.testing.allocator;
const wasm_bytes = try std.fs.cwd().readFileAlloc(allocator, "my_app.wasm", 1024 * 1024);
defer allocator.free(wasm_bytes);
var module = try wasm.parse(wasm_bytes, allocator);
defer module.deinit();
std.debug.print("Wasm module version: {d}\n", .{module.version});
}

4. 示例:检查模块的导入和导出

Section titled “4. 示例:检查模块的导入和导出”

解析Wasm模块的一个常见用途是了解它需要从宿主环境获得什么(导入),以及它向宿主环境提供了什么(导出)。

// ...接上文
std.debug.print("--- Imports ---", .{});
for (module.imports) |imp| {
std.debug.print("Module: {s}, Name: {s}, Type: {any}\n", .{
imp.module, imp.name, imp.kind
});
}
std.debug.print("--- Exports ---", .{});
for (module.exports) |exp| {
std.debug.print("Name: {s}, Type: {any}\n", .{exp.name, exp.kind});
}
  1. 编写一个简单的Zig函数,例如fn add(a: i32, b: i32) i32,并将其export。
  2. 将其编译为Wasm模块。
  3. 编写一个简单的HTML文件,使用JavaScript的WebAssembly.instantiateStreaming来加载和实例化你的Wasm模块。
  4. 在JavaScript中调用你导出的add函数并打印结果。
  • 线性内存(Linear Memory) Wasm模块在一个隔离的、沙箱化的内存空间中运行,这被称为线性内存。它本质上是一个可调整大小的ArrayBuffer。Zig代码中的指针都变成了这个线性内存中的索引。Wasm模块可以导出一个memory对象,让宿主(如JavaScript)可以直接读写这块内存,这是实现复杂数据交换的关键。

  • WASI(WebAssembly System Interface) freestanding目标意味着Wasm模块不能访问文件、网络或控制台等系统资源。WASI是一个标准化的接口,旨在为Wasm提供这种系统级的能力。通过将目标设置为wasm32-wasi,Zig可以编译出能够使用WASI主机实现的Wasm模块,使其可以在服务器端或命令行中运行。

  • 栈机(Stack Machine) Wasm是一种基于栈的虚拟机。它的指令从一个虚拟的栈中弹出操作数,并将结果推回栈中。Zig编译器负责将你的过程式代码高效地翻译成这种基于栈的指令序列。

7. 总结:超越浏览器的可移植目标

Section titled “7. 总结:超越浏览器的可移植目标”

WebAssembly正在迅速成为一种通用的、可移植的、高性能的编译目标,其应用场景已远远超出了Web浏览器。

Zig与Wasm的结合是天作之合。Zig的简洁性、性能和对内存的精细控制,使其能够生成极其小巧和高效的Wasm模块。无论是为Web前端编写性能关键的计算密集型代码,还是为边缘计算、插件系统或无服务器平台构建安全、可移植的组件,Zig都提供了一流的工具链和语言支持,让你能够轻松地拥抱WebAssembly的未来。