NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

基础为零?如何将 C++ 编译成 WebAssembly(一)

  • 2019-12-16
  • 本文字数:2148 字

    阅读完需:约 7 分钟

基础为零?如何将 C++ 编译成 WebAssembly(一)


本文知识点提炼:

1、如何使用 Emscripten 把 C++ 编译成 wasm。

2、如何使用 wasi-sdk 把 C++ 编译成 wasm。

3、如何运行编译好的 wasm 包。


WebAssembly 是一个 W3C 推出的二进制指令格式,近日它的 1.0 版本也正式定稿成为了规范,关于它的基本概念这里不再展开介绍了,网上已经有很多文章了,大家可以自行了解,推荐阅读官方文档①、spec 仓库②、MDN 的教程③、以及 Lin Clark 的文章④,其他随意。


能编译成 wasm 的语言有很多,C++ 和 Rust 是其中两个比较成熟而且大量被使用的,本文以 C++ 为例,一步一步介绍如何把 C++ 代码编译成 wasm 并且运行起来。例子很简单,相信不了解 C/C++ 开发的同学也能看懂。


说是 C++ 其实本文用到的代码都是纯 C 的。


文章用到的源码和编译脚本都在: https://github.com/Hanks10100/cpp2wasm


Hello World!


首先,我们来编译一个 C 语言的 Hello World,创建一个 hello.c 文件:


#include <stdio.h>
int main() { printf("Hello World!\n"); return 0;}
复制代码

▐ 编译成可执行文件

代码就是输出了一句 Hello World! ,使用 clang 或 gcc 或很多工具都可以把这段代码编译成可执行的二进制,找不到命令的话,可以在网上找教程配置一下。以 clang 为例:


clang hello.c -O3 -o out/hello
复制代码


-O3 表示了优化级别, 生成的可执行文件是 hello ,但是这个文件只能在特定平台上执行,在 windows 上编译出来的文件没办法跑在 mac 上(不绝对),在 32 位系统编译出来的文件无法跑在 64 位系统上。


然而如果把它编译成 wasm 就可以跨平台分发了,这也是 wasm 的一大优势。只需要编译一次,同一个 wasm 包,可以运行在浏览器中、Node.js 中、各种独立的 runtime 里,但是要求目标平台具备执行 wasm 包的能力,而且符合规范。

▐ WebAssembly 的编译和运行流程

在编译 WebAssembly 之前先了解一下它基本的编译和运行流程,想要以何种方式运行 wasm 的包,决定了以何种方式来编译它。


目前来看,大部分使用 WebAssembly 的例子都是运行在浏览器中的,有一部分运行在 Node.js 里,和 JS 的渊源很深,因为在标准里定义了一套 JS API 来编译、实例化 wasm 文件,这部分 API 已经被 JS 引擎实现了,功能已经稳定可用。因此,wasm 最常见的是搭配 js 一起使用,这种场景下用 Emscripten ⑤ 可以搞定,它在编译 wasm 包的同时也会生成一份 js “glue” 代码,把 wasm 包的初始化接口导入导出都封装在 js 里了,使用时引入这个 js 文件即可。


Emscripten 也支持编译成独立的 wasm 包(不含 JS),但是想要运行这个 wasm 包需要宿主环境给它注入很多基础的 API,而且这些  API 是非标准的。如果想在 JS 环境里运行独立 wasm 包的话,要用 JS 实现这些 API。
复制代码


其实 WebAssembly 本质上和 JS 无关,完全可以运行在独立的沙箱环境里,通过标准化的 API (wasi ⑥) 来调用系统能力。现在已经有不少 wasm 的独立运行时了,如 Wasmtime ⑦ 和 wasm-micro-runtime ⑧,它们都可以加载并独立执行 wasm 文件,并且实现了一致的 wasi 接口。


关于 wasi,推荐阅读《Standardizing WASI: A system interface to run WebAssembly outside the web》


https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/



如上图所示,面对自己的 C/C++ 代码,想要把它运行在浏览器或 Node.js 中,就使用 Emscripten 把它编译成 wasm + js 文件;想要把它运行在独立的运行时里,就使用 wasi-sdk ⑨ 进行编译,生成单独的 wasm 包。(此结论简单粗暴,为了方便理解,并不严谨)

▐ 使用 Emscripten 编译

首先安装官方文档安装 Emscripten (https://emscripten.org/) ,安装完成后命令行环境里会有 emcc 命令,使用方式和 gcc 差不多,执行如下代码就可以生成 wasm 的包:


emcc hello.c -O3 -o out/hello-emcc.wasm
复制代码


但是,上面这个命令隐含了 -s STANDALONE_WASM 的配置 ,实际上触发的是 WebAssembly Standalone build ⑩,只生成了一个 wasm 的包,需要自己写 loader 加载和执行。如果不想费这个劲,就可以使用如下命令直接生成 wasm + js 文件:


emcc hello.c -O3 -o out/hello-emcc.js
复制代码


该命令除了生成 js 文件以外,还会生成同名的 hello-emcc.wasm 文件,可以使用 WABT ⑪ (WebAssembly Binary Toolkit) 提供的小工具把 wasm 文件转成对等的文本格式,方便阅读。


wasm2wat out/hello-emcc.wasm -o out/hello-emcc.wat
复制代码


代码比较短,但是生成出来的 wasm 文件有 2.1KB,js 文件 16KB,主要是因为 stdio.h 头文件里有很多依赖,在运行时是由 js 代码来实现的。用 wasm 做 io 本身也不是个好的用法。


最后,直接在 Node.js 环境里执行这个 js 文件就行了,可以看到控制台输出了 Hello World! 。


node out/hello-emcc.js

▐ 使用 wasi-sdk 编译

首先根据自己的系统下载相应的 wasi-sdk ,配置好环境变量之后,就可以调用其中自带的 clang 工具编译生成 wasm 文件:


clang hello.c -O3 -o out/hello-wasi.wasm
复制代码


大概率跑不通…… 因为要配各种环境变量还要指定 sysroot 才行。假如你下载的是 8.0 版本,放到了个人目录之下,可以用下面这个命令编译代码,不需要配置环境变量:


~/wasi-sdk-8.0/bin/clang --sysroot ~/wasi-sdk-8.0/share/wasi-sysroot
复制代码


本文转载自淘系技术公众号。


原文链接:https://mp.weixin.qq.com/s/XrOHuoJB4vwkozBDI4t1yA


2019-12-16 18:291148

评论

发布
暂无评论
发现更多内容

Java完全自学手册,从外包到大厂,再到年薪100万技术大佬都靠它

JAVA活菩萨

Java 程序员面试 大厂技能 秋招 大厂面经

leetcode 20. Valid Parentheses 有效的括号(中等)

okokabcd

LeetCode 数据结构与算法 栈和队列

从TRPO到PPO(理论分析与数学证明)

行者AI

深圳堡垒机厂家有哪些?重点推荐哪家?

行云管家

网络安全 堡垒机 深圳 运维审计

兆骑科创创业大赛竞赛平台,双创服务,投融资对接

兆骑科创凤阁

vivo手机上的系统级消息推送平台的架构设计实践

JackJiang

网络编程 架构设计 消息推送 即时通讯 即时通讯IM

开源一夏│别逗,作为程序员你竟还没参与过开源项目?

开源 经验分享 签约计划第三季 8月月更

兆骑科创创新创业大赛平台,人才引进,项目路演

兆骑科创凤阁

不得不服!真心被这份阿里大牛开源的“全彩版图解HTTP手册”折服了

JAVA活菩萨

Java 程序员面试 大厂技能 秋招 大厂面经

结合实际聊聊电平转换电路(常用电平转换电路总结)

矜辰所致

电路设计 8月月更 电平转换电路

关于使用WebStorm两年所总结的一些常用插件和功能

安安安

前端 webstorm

Github爆火!程序员疯抢的Java面试宝典(PDF版)限时开源

Java工程师

Java 面试 大厂

R7 6800H标压处理器+RTX 3050独显 无畏Pro15锐龙版高能开卖

科技热闻

从函数计算到 Serverless 架构

hum建应用专家

云计算 Serverless

国际计费系统基于Sharding-Proxy大数据迁移方案实践

京东科技开发者

数据库 系统 数据迁移

双Q合璧:RabbitMQ与RocketMQ,电子版手绘脑图+学习指南+面试等

冉然学Java

RocketMQ RabbitMQ 架构设计 笔记 java 日志

架构师学习心得总结

泋清

#架构实战营

看完腾讯大佬90天整理的“Redis深度笔记”,我直接当场膜拜

Java工程师

Java redis 源码

SpringMVC(二、请求和响应)

开源 springmvc 8月月更

Dapp系统开发及智能合约部署技术

薇電13242772558

智能合约 dapp

【大厂面试真题解析】蔚来数字化业务后端一面(2022.8.6)

面试官问

后端 面试题 大厂面试 面经分享 蔚来

6大论坛,30+技术干货议题,2022首届阿里巴巴开源开放周来了!

阿里技术

数据库 云计算 大数据 #开源

腾讯SpringBoot高阶笔记,限时开源48小时

冉然学Java

编程 springboot 笔记 java 日志 #开源

dubbo 长连接

石臻臻的杂货铺

dubbo 8月月更

太厉害了!华为大牛终于把MySQL讲的明明白白(基础+优化+架构)

冉然学Java

Java MySQL 编程 架构 微服务

Rust 入门指南(使用JSON)

王泰

rust

面向推荐的汽车知识图谱构建

之家技术

人工智能 机器学习 知识图谱 汽车

宁夏等保测评机构有哪些?如何选择?

行云管家

等保 堡垒机 等级保护 等保测评 宁夏

爆了!1213页LeetCode算法刷题神册(全彩),GitHub万星仅是开始

JAVA活菩萨

Java 程序员面试 大厂技能 秋招 大厂面经

数据建模已死,真的吗?

Kyligence

数据建模 数据模型 数据指标

.NET现代应用的产品设计 - DDD实践

MASA技术团队

.net DDD 实战 MASA Framewrok

基础为零?如何将 C++ 编译成 WebAssembly(一)_文化 & 方法_张翰(门柳)_InfoQ精选文章