什么是 wasm wasi wapm 技术
什么是 wasm
wasm官网: https://webassembly.org/
wasm 全称 WebAssembly,是一种通用字节码的技术,通过该技术将其他语言(比如 go, rust, c/c++, 等)的程序代码编译成可以直接在浏览器环境执行的字节码程序。
因为该 WebAssembly 是一种字通用的节码技术规范,所以 WebAssembly 不仅仅可以在浏览器执行,还可以在其他变成语言环境中执行,比如 ,python, c,go, rust,等.
为什么需要搞一个 wasm 字节码,不能所以平台都用 js 做为 通用编码技术吗?
理论上 在其他语言环境中实现 js 执行引擎, 是可以把 js 做为一个 通用编码技术的。
那为什么还需做一个 wasm 出来呢。 我认为有以下几点原因:
- js 代码是文本文件格式。在 web 环境下,网络传输效率远远不如 wasm 二进制数据传输。
- js 相对于 c 和 rust ,而已,是一种弱类型语言,写代码没有严格类型校验,容易写成不稳定的代码。
- js 是有 GC(自动垃圾清理功能),用户不用关注内存的处理,导致的结果就是 GC 处理过程严重拖慢性能, 而且由于是系统 GC, 用户很难精确控制内存。容易导致内存占用过高。
- js 是文本解析型编程语言,文本解析效率低,wasm 二进制代码解析执行效率会高于 js。
- js 由于历史原因,js 本身就存在一些缺陷(js 之父说的),而且 js 引擎需要实现的成本过高, wasm 的执行引擎可以说是 比 js 新的产物, 在设计 wasm 字节码的时候,考虑各个方便比较完善,漏洞也比较少。
什么是 wasi
wasi官网: https://wasi.dev/
wasi 全称 The WebAssembly System Interface 通俗来讲就是一个对接规范。
就像前后端分离的 RestFul 类似,只要前后端约定一套 RESTful 接口对接规范 ,无论前端使用的框架是 jQuery 或 vue 或 react , 无论后端使用 java node.js 或者 java 还是 go。 都可以进行前后端通信。
wasi 也是同样的道理, wasi 约定了也行函数接口,不同的语言代码的生成的字节码规则是一致的,这样就可以在其他平台执行这个字节码。
举个栗子:
比如我用 c语言 写了一个程序,读取某个文件, 把文件内容输出到屏幕。
然后我把这个程序编译成 wasm 字节码。 然后让 web 浏览器去执行。
我们都知道 浏览器是不可以直接读取系统文件的。那么 web 浏览器应该如何去执行这个字节码程序呢?
通过 wasi (后文会介绍) 协议,我们可以在 浏览器环境中用 JavaScript 写 读取文件的 API 回调函数,然后在执行字节码程序之前,把这个 options 传给 wasm 执行引擎。 js 中可以实现 文件读取逻辑(可能是从网络中读取,也可以返回一段字符串)。
然后 web 浏览器 就可以无障碍的执行这段 字节码程序了。

wasm的好处:
- 可以移植其他平台的代码给不同平台执行(跨平台跨语言执行能力)
- 可以用到其他语言的特性,给浏览器执行环境赋能,比如通过 rust 生成的 wasm,有更高的执行效率和内存安全特性。
- 通过 wasi 可以在浏览器端实现模拟 文件系统, sock 网络服务等, 实现类似 webide
如何生成 wasm 字节码程序
因为 wasm 是一种通用的字节码交互技术,大部分流行的语言的环境的 sdk 工具包中就内置了 wasm 相关的工具。 比如 go 语言中可以通 GOOS=js GOARCH=wasm go build -o static/main.wasm
生成 wasm 字节码程序。
另外还有其他第三方或者 wasm官方的工具。目前比较通用的一个工具是:wasmtime
wasmtime官网文档: https://docs.wasmtime.dev/
这里简单举个栗子 用 Rust 生成字节码。
第一步我们需要安装 Rust 语言环境:
安装 rust 文档
第二步 安装 rust 的 wasm 工具
1
| $ cargo install cargo-wasi
|
第三步 创建 rust hello 项目
1 2
| $ cargo new hello-world $ cd hello-world
|
修改 src/main.rs 添加导出函数
1 2 3
| pub fn main() { println!("Hello, world! wasm!"); }
|
调试运行 rust wasi 字节码程序
1 2 3 4 5 6 7 8 9
| $ cargo wasi run info: downloading component 'rust-std' for 'wasm32-wasi' info: installing component 'rust-std' for 'wasm32-wasi' Compiling hello-world v0.1.0 (/hello-world) Finished dev [unoptimized + debuginfo] target(s) in 0.16s Running `/.cargo/bin/cargo-wasi target/wasm32-wasi/debug/hello-world.wasm` Running `target/wasm32-wasi/debug/hello-world.wasm` Hello, world!
|
通过 wasmtime 工具 运行 wasi 字节码程序。
需要安装 wasmtime 工具, 安装文档:https://docs.wasmtime.dev/cli-install.html
1 2
| wasmtime target/wasm32-wasi/debug/hello-world.wasm Hello, world!
|
到此为止我们就实现生成了 wasm 字节码程序了
为什么我更推荐使用 rust 字节码程序。
因为 rust 是一种强类型,性能比较优秀的的 编程语言。 和 c 语言一样没有 gc,但是 rust 在内存操作方面,rust 编译器有优秀的 语法检查机制,所以可以让用户写出优秀的内存安全代码。
而且 rust 和 c 语言相比, 由于 rust 是后辈, 所以 rust 有很多新的现代语言特性, 可以通过简单的代码语法糖,实现 c语言的需要很多代码实现的功能。
如何在 nodejs 环境执行 wasm 代码
nodejs 16 内置了 WebAssembly API
在 刚刚的 rust 项目下, 创建 一个 test.js 脚本, 写入如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
| (async () => { try{
const fs = require('fs'); const path = require('path');
const env = { PWD: '/' };
let thisView = null; let wasmRes = null; let thisMemory = null; let consoleLogData = '';
const refreshMemory = () => { if (!thisView || thisView.buffer.byteLength === 0) { thisView = new DataView(thisMemory.buffer); } }
const getiovs = (iovs, iovsLen) => {
refreshMemory(); const buffers = Array.from({ length: iovsLen }, (_, i) => { const ptr = iovs + i * 8; const buf = thisView.getUint32(ptr, true); const bufLen = thisView.getUint32(ptr + 4, true); return new Uint8Array(thisMemory.buffer, buf, bufLen); }); return buffers; };
function Uint8ArrayToString(fileData){ var dataString = ""; for (var i = 0; i < fileData.length; i++) { dataString += String.fromCharCode(fileData[i]); } return dataString }
const importObject = { wasi_snapshot_preview1: { fd_write: (fd, iovs, iovsLen, nwritten) => { let written = 0; const buffers = getiovs(iovs, iovsLen);
buffers.forEach(buffer => { consoleLogData+= Uint8ArrayToString(buffer); written+=buffer.length; });
thisView.setUint32(nwritten, written, true); return 0; }, environ_get: (environ, environBuf) => { refreshMemory(); let coffset = environ; let offset = environBuf; Object.entries(env).forEach(([key, value]) => { thisView.setUint32(coffset, offset, true); coffset += 4; offset += Buffer.from(thisMemory.buffer).write( `${key}=${value}\0`, offset ); }); return 0; }, environ_sizes_get: (environCount, environBufSize) => { refreshMemory(); const envProcessed = Object.entries(env).map( ([key, value]) => `${key}=${value}\0` ); const size = envProcessed.reduce( (acc, e) => acc + Buffer.byteLength(e), 0 ); thisView.setUint32(environCount, envProcessed.length, true); thisView.setUint32(environBufSize, size, true); return 0; }, proc_exit: (rval) => { return 0; }, } };
const wasmFileBuffer = fs.readFileSync(path.join(__dirname, 'target/wasm32-wasi/debug/code-stock.wasm'));
wasmRes = await WebAssembly.instantiate(wasmFileBuffer, importObject); thisMemory = wasmRes.instance.exports.memory;
wasmRes.instance.exports.main();
console.log({ consoleLogData, }); } catch(e) { console.error(e); } })();
|
然后直接通过 node test.js
既可执行 wasm 代码
如何在浏览器环境执行 wasm 程序
目前新版本的现代浏览器基本上都有 WebAssembly API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| (async () => { try{ const env = { PWD: '/' }; let thisView = null; let wasmRes = null; let thisMemory = null;
let consoleLogData = '';
const refreshMemory = () => { if (!thisView || thisView.buffer.byteLength === 0) { thisView = new DataView(thisMemory.buffer); } }
const getiovs = (iovs, iovsLen) => {
refreshMemory(); const buffers = Array.from({ length: iovsLen }, (_, i) => { const ptr = iovs + i * 8; const buf = thisView.getUint32(ptr, true); const bufLen = thisView.getUint32(ptr + 4, true); return new Uint8Array(thisMemory.buffer, buf, bufLen); }); return buffers; };
function Uint8ArrayToString(fileData){ var dataString = ""; for (var i = 0; i < fileData.length; i++) { dataString += String.fromCharCode(fileData[i]); } return dataString }
const importObject = { wasi_snapshot_preview1: { fd_write: (fd, iovs, iovsLen, nwritten) => { let written = 0; const buffers = getiovs(iovs, iovsLen);
buffers.forEach(buffer => { consoleLogData+= Uint8ArrayToString(buffer); written+=buffer.length; });
thisView.setUint32(nwritten, written, true); return 0; }, environ_get: (environ, environBuf) => { refreshMemory(); let coffset = environ; let offset = environBuf; Object.entries(env).forEach(([key, value]) => { thisView.setUint32(coffset, offset, true); coffset += 4; offset += Buffer.from(thisMemory.buffer).write( `${key}=${value}\0`, offset ); }); return 0; }, environ_sizes_get: (environCount, environBufSize) => { refreshMemory(); const envProcessed = Object.entries(env).map( ([key, value]) => `${key}=${value}\0` ); const size = envProcessed.reduce( (acc, e) => acc + Buffer.byteLength(e), 0 ); thisView.setUint32(environCount, envProcessed.length, true); thisView.setUint32(environBufSize, size, true); return 0; }, proc_exit: (rval) => { return 0; }, } };
const wasmFileBuffer = fetch('target/wasm32-wasi/debug/code-stock.wasm');
wasmRes = await WebAssembly.instantiate(wasmFileBuffer, importObject); thisMemory = wasmRes.instance.exports.memory;
wasmRes.instance.exports.main();
console.log({ consoleLogData, }); } catch(e) { console.error(e); } })();
|
什么是 wapm
wamp官网: https://wapm.io/
wapm 全称是 webassembly Package management , 是一个线上的 wasm 二进制包管理仓库。作用类似于 node.js 的 npm 或 java 的 maven
我们可以在这个仓库上 发布自己的 wasm 程序,或者下载运行别人发布的 wasm 程序。
git repo
原文链接: http://blog.jajabjbj.top/2022/07/10/wasm/
版权声明: 转载请注明出处.