WebAssembly和Emscripten入门简介

初识WebAssembly

WebAssembly,简称 WASM ,是一种新的低级二进制格式,它可以在所有主流的浏览器中使用,包括 Chrome,Firefox,Edge和Safari。WebAssembly最初由Mozilla、Google、微软和其他技术公司推出,它的目标是通过提供一种高性能、可移植和安全的方式来扩展Web平台,以便开发人员可以在Web上构建更加复杂和功能更加强大的应用程序。

WebAssembly与JavaScript不同,它是一种二进制格式,而不是文本格式,它需要使用虚拟机来解释字节码,同时提供与Web技术的交互。WebAssembly可以将C/C++、Rust等语言编写的代码编译成字节码格式,然后在浏览器中运行。WebAssembly可以通过使用JavaScript的API调用,也可以在WebAssembly模块之间进行通信。WebAssembly可以更快地加载、解析和执行,从而提供更好的性能和更低的内存占用。

Emscripten开发入门

WebAssembly需要将C/C++、Rust等语言编写的代码编译成字节码格式,然后在浏览器中运行。Emscripten是一个将C/C++代码转换为WebAssembly的工具集,是WebAssembly技术的重要推动者之一。Emscripten的核心是一个将LLVM字节码转换为JavaScript或WebAssembly字节码的编译器,称为LLVM到JavaScript的编译器。该编译器可以将C/C++代码编译为LLVM字节码,然后通过LLVM到JavaScript编译器将字节码转换为JavaScript或WebAssembly代码。通过利用Emscripten工具,开发者可以使用C/C++等语言来开发Web应用,利用WebAssembly的高性能、低资源消耗、跨平台等优势来提升Web应用的性能和用户体验。

使用Emscripten开发编译流程大致如下:

Emscripten安装

安装Emscripten需要使用emsdk脚本,emsdk脚本是基于python语言开发的,安装前需要确认有以下依赖项:gitpythonNode.js

安装步骤如下:

在终端中输入以下命令

bash 复制代码
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
git pull
./emsdk install latest
./emsdk activate latest

配置环境变量

bash 复制代码
source ./emsdk_env.sh

安装完成后的目录

安装结束后进行版本校验,查看版本信息

复制代码
emcc -v

Hello, Emscripten

接下来我们开始Emscripten开始之旅,新建一个hello.cpp文件,代码如下:

c 复制代码
// hello.cpp

#include <iostream>

using namespace std;

int main() {
    cout << "Hello, World!" << endl;
    return 0;
}

切换到当前工程目录,在控制台使用emcc命令生成wasm文件,通过-o选项指定输出文件:

复制代码
emcc hello.cpp -o hello.js

编译完成后在目录中会生成hello.js文件和hello.wasm文件。 被编译成WebAssembly后的C/C++代码是不能直接运行的,需要通过浏览器加载运行。 在同一个目录,我们继续新建一个hello.html文件,并引入hello.js文件:

xml 复制代码
// hello.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script src="hello.js"></script>
</body>
</html>

在浏览器中打开hello.html文件,在开发者面板中的Console中能够看到hello.cpp的输出内容。

WebAssembly是一种跨平台的字节码,不仅可以在浏览器中运行,也可以在node环境中运行:

WebAssembly和JavaScript交互

如果要实现一个实用功能的WebAssembly模块,必然需要提供能够和外部交互的函数接口。包括JavaScript调用C/C++函数功能,C/C++函数调用JavaScript方法,以及C/C++函数和JavaScript交换数据等需求。

Emscripten编译生成的js文件是一种胶水代码,用来加载WebAssembly模块和导出相关函数。生成的js代码中有一个全局对象Module,该全局对象是Emscripten的核心,负责WebAssembly模块的载入、创建初始化等,以及对C/C++函数进行封装等功能。在之前hello.js中有对hello.cpp中的main函数的封装代码如下:

Emscripten会对封装导出的函数前面加上下划线,main()函数会被封装成_main()。我们可以在浏览器控制台中直接使用_main()函数。

JavaScript调用C/C++函数

接下来我们新建一个export.cpp文件,代码如下:

arduino 复制代码
// export.cpp

#ifndef EM_PORT_API
#    if defined(__EMSCRIPTEN__)
#        include <emscripten.h>
#        if defined(__cplusplus)
#            define EM_PORT_API(rettype) extern "C" rettype EMSCRIPTEN_KEEPALIVE
#        else
#           define EM_PORT_API(rettype) rettype EMSCRIPTEN_KEEPALIVE
#        endif
#    else
#        if defined(__cplusplus)
#            define EM_PORT_API(rettype) extern "C" rettype
#        else
#           define EM_PORT_API(rettype) rettype
#        endif
#    endif
#endif

EM_PORT_API(int) add(int a, int b)
{
    return a + b;
}

我们定义了一个EM_PORT_API的宏定义,用来统一C和C++环境,方便用来导出函数。其中__EMSCRIPTEN__是用来识别Emscripten环境,__cplusplus是用来判断C++代码,EMSCRIPTEN_KEEPALIVE是Emscripten的宏定义,用来优化编译的。

切换到工程目录,在控制台使用emcc命令生成wasm文件:

arduino 复制代码
emcc export.cpp -o export.js  

目录中生成文件:export.jsexport.wasm。继续在浏览器中测试我们的代码,新建一个export.html文件,代码如下:

xml 复制代码
// export.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script async src="export.js"></script>
    <script>
        Module = {}
        Module.onRuntimeInitialized = function() {
            console.log(Module._add(123, 456))
        }
    </script>
</body>
</html>

WebAssembly实例是通过方法createWasm()异步创建的,有可能js文件加载后,Emscripten运行环境没有准备好,我们需要通过建立一种Emscripten运行时准备就绪的机制。一种简单的方法就是使用onRuntimeInitialized 回调方法,在这个回调方法中运行WebAssembly Module导出的方法。

通过运行浏览器,我们可以在控制台查看JavaScript调用C++代码的结果:

C/C++函数调用JavaScript方法

反过来,我们也可以在C/C++函数调用JavaScript方法。首先需要创建一个call_js.cpp文件,代码如下:

arduino 复制代码
// call_js.cpp

#ifndef EM_PORT_API
#    if defined(__EMSCRIPTEN__)
#        include <emscripten.h>
#        if defined(__cplusplus)
#            define EM_PORT_API(rettype) extern "C" rettype EMSCRIPTEN_KEEPALIVE
#        else
#           define EM_PORT_API(rettype) rettype EMSCRIPTEN_KEEPALIVE
#        endif
#    else
#        if defined(__cplusplus)
#            define EM_PORT_API(rettype) extern "C" rettype
#        else
#           define EM_PORT_API(rettype) rettype
#        endif
#    endif
#endif

// declare the function in the cpp file
// function definition in js file
EM_PORT_API(int) js_add(int a, int b);
EM_PORT_API(void) js_console_log(int a);

// define the function in the cpp file
EM_PORT_API(void) call_js_func() {
    int ret = js_add(11, 22);
    js_console_log(ret);
}

我们在call_js.cpp文件中声明了js_addjs_console_log两个函数,这两个函数/方法的具体实现是在js文件中。接着新建一个mylibrary.js文件,代码如下:

javascript 复制代码
// mylibrary.js

mergeInto(LibraryManager.library, {
    js_add: function (a, b) {
        console.log("js_add");
        return a + b;
    },
    js_console_log: function(a) {
        console.log("js_console_log: ", a);
    }
})

按照在C++文件中声明的方法签名,在js文件中实现了js_addjs_console_log的方法功能,并合并注入到LibraryManager.library中。LibraryManager.library对象可以简单理解为是JavaScript注入到C/C++中的运行时库。

在控制台使用emcc命令生成wasm文件,需要使用--js-library链接外部的js文件:

css 复制代码
emcc call_js.cpp --js-library mylibrary.js -o call_js.js

继续新建一个call_js.html文件用来测试代码,在浏览器查看我们的运行结果。

xml 复制代码
// call_js.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script async src="./call_js.js"></script>
    <script>
        Module = {}
        Module.onRuntimeInitialized = function() {
            Module._call_js_func();
        }
    </script>
</body>
</html>

浏览器的运行结果如下:

总结

我们简单的展示了C/C++和JavaScript函数之间通过WebAssembly的相互调用,在这里并没有涉及到复杂的数据交换和相关的内存模型操作。WebAssembly是一个强大的Web应用技术,可以为Web应用程序提供更好的性能和更多强大的功能。Emscripten是一个用来开发WebAssembly应用的强大工具集。通过不断的学习WebAssembly开发技术,可以帮助开发人员在Web平台上构建更快、更安全、更可移植的应用程序。

相关推荐
AI大模型-小华19 分钟前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子3 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅3 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni3 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学4 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端
赵庆明老师5 小时前
Vben精讲:21-详解web-antd:tsconfig.json
前端·json·vim
wc885 小时前
微软EDGE浏览器功能学习
前端·学习·edge
Csvn5 小时前
🎯 原生 `<dialog>` 元素:终于可以扔掉一半的自定义弹窗组件了?
前端
Csvn5 小时前
🎨 CSS @layer:用「层叠优先级」终结样式打架的世纪难题
前端