本文是《WebAssembly 权威指南》系列文章第 2 篇,系列文章列表:
学习 WebAssembly 的难点在于入门资料很多。作为一名 C/C++ 或 Rust 开发者,你都可以从这里开始,但也可以直接了解 WebAssembly 的机制,不需要考虑语言生成的问题。本章将从不考虑语言的角度介绍 WebAssembly,随着我们深入学习底层细节,在后面的章节中,我们将与高级语言建立联系。虽然这些细节最初看起来简单,但对于理解 WebAssembly 的基本机制却非常重要,它们不会是你的最终目的。
在 第 1 章 [1],我曾经在讨论 asm.js 时介绍了大多数人编写新编程语言或技术的第一个程序。在例 2-1 中,我们再次展示了这个程序,它被称为 "Hello, World!",以向 Brian Kernighan 和 Dennis Ritchie 的开创性著作《C 程序设计语言》致敬。很多优秀的编程书籍都从这个例子开始,因为它使读者了解了程序的基本概念,而不会深入细节。这是一种有趣,简洁的方法,也是确保读者正确设置工具的有效方法。
例 2-1. 典型的 "Hello, world!" 程序,用 C 语言编写
#include <stdio.h>
int main () {printf ("Hello, World!\n");
return 0;
}
不幸的是,WebAssembly 没有办法打印到控制台,所以我们不能以这种方式开始。
等等,什么?
我将给你一点时间来消化这句话,也许可以重读几遍,以确保你能理解。
是不是感觉很困惑?是的,可以说,WebAssembly 没有办法打印到控制台、读取文件或打开网络连接...... 除非有对应的 API 可以调用。
在检查例 2-1 时,你会发现问题所在。要使该程序正常运行,它需要 printf () 函数的工作副本,这个函数可以在标准库中找到。C 语言程序具有可移植性的原因之一是,这种标准库在各种平台上都存在。POSIX 库,把这些常见的功能扩展到了控制台以外,包括文件操作、信号处理、消息传递等。应用程序会写入诸如 POSIX 的 API,但可执行文件还需要一个静态或动态库,提供在目标平台上运行的调用方法的行为,这就是你打算使用的操作系统的本地可执行格式。
因此,WebAssembly 使代码可移植,但我们仍然需要其他工具来帮助使应用程序可移植。我们会在书中重新探讨这个话题,但现在请记住,WebAssembly 没有直接的方法来写入控制台。
在第 5 章 [2],我们将向 Kernighan 和 Ritchie 致敬,并运行那个程序。但首先,我们需要学习 WebAssembly 的人类友好格式,以及如何让低级指令与堆栈机交互。然而,我仍然希望你在这里有一个 “Hello, world!” 的体验,所以我们将挑选其他内容来编写和运行,这不是太有挑战性,但仍然是合理的。这也算是一种 "Hello, world!" 吧!
WebAssembly 文本格式(Wat)
WebAssembly 的二进制格式(Wasm)的设计目的是为了加速 WebAssembly 模块的传输、加载和验证。我们将在 第 3 章 [3] 更深入地了解模块,但现在只需把它们理解为部署的单元,就像 Java 中的库或 Jar 文件。另外,还有一种描述模块行为的文本格式,方便人类阅读,这就是 Wat。虽然你可以手写文本格式的代码,但很少有人这样做。这种格式有时也被称为 "Wast",但这是原名。很多工具都支持这两种写法,因此容易混淆。本书将使用 Wat 和它的后缀 .wat。
例 2-2 展示了一个完整的、有效的使用 Wat 表达的 Wasm 模块。它通过函数签名和堆栈机指令的集合来表示,类似于 Lisp 格式。WebAssembly 抽象机是一个虚拟堆栈机,在后面我们将对此进行详细解释。大多数编译后的软件都会被编译成特定硬件架构的可执行格式。例如,如果你的目标是 Intel x86 机器,那么代码将被编译为一系列的指令,可以在该芯片上