chrome浏览器 docker_浏览器中执行 C 语言?WebAssembly 实践

本文介绍如何使用 WebAssembly 在浏览器中运行 C 语言,通过 Emscripten 将 C 代码编译为 WebAssembly,实现前端的计算密集型任务,例如 MD5 计算和文件解压。文章详细讲解了从 Hello World 到使用 libarchive 解析压缩文件的过程,涉及内存管理、JavaScript 调用 C 函数、C 调用 JavaScript 函数以及 64 位数值处理等关键点。
摘要由CSDN通过智能技术生成

a004b697855db6987f36639797f39982.png

0020168c5dd0f1605ed8a77eed441afd.png
wyf
网易游戏高级开发工程师,负责基础架构平台产品的前端开发工作。

在最近的一个项目开发中,需要在前端实现计算文件 MD5 和解析压缩包文件目录的功能。在库的选择上,JavaScript 有 spark-md5 库用于计算 MD5,但是对于上 G 大小的文件经常需要数十秒甚至几分钟的计算时间。至于解析压缩文件,虽然一些库可以来解压 zip,而项目还需解析 7z 格式,这方面没有现成的 JavaScript 库可以使用。

对于 MD5 计算等计算密集的任务、文件解压等基础功能,往往能找到相应的 C 语言的库,而各个主流浏览器在 2017 年开始支持的 WebAssembly 为 C 在浏览器中执行其他语言提供了可能。

WebAssembly 是除了 JavaScript 外,另一种可以运行在浏览器中的语言,它是一种低级的类汇编语言,可以接近原生的性能运行。WebAssembly 作为诸如 C/C++/Rust 等语言的编译目标,使它们可以以 WebAssembly 的形式在浏览器中执行。

在项目中最终使用 WebAssembly 实现了 MD5 的计算,计算耗时减少了 60%,并且使用了 libarchive 进行了压缩文件解析。下面对这个过程中的一些经验和问题进行回顾。

(点击阅读原文可以查看 demo)

Hello World

我们尝试一个简单的例子,首先编写一段 C 的 hello world 代码:

/* main.c */
#include <stdio.h>

int main() {
  printf("hello worldn");
  return 0;
}

使用 Emscripten 编译。Emscripten 的安装可以参考文档,为了方便和统一我们使用一个已经构建好的 docker 镜像 trzeci/emscripten。

docker run --rm -v $(pwd):/working trzeci/emscripten 
  emcc /working/main.c -o /working/index.html

emcc 即 Emscripten 提供的类似 gcc 的编译器,这里我们设置输出为 index.html,则 emcc 会输出:

  • index.wasm,由 C 编译生成的 WebAssembly 二进制文件
  • index.js,Emscripten 提供的封装了浏览器 WebAssembly API 的 JavaScript 代码
  • index.html,Emscripten 提供的 html 入口

编译成功后,启动一个静态服务器:

# 使用 Node.js
npx serve .
# 或使用 Python
python -m SimpleHTTPServer 8000

用浏览器访问服务器,即可看到代码执行的结果:

cd3f1f624a6e331d597bc90ef09bf6c2.png

在 2017 年 Firefox、Chrome、Safari 和 Edge 等浏览器均支持了 WebAssembly API。WebAssembly API 主要包括:

  • WebAssembly 的加载和执行
  • 函数的 import / export
    WebAssembly 可以 import JS 中的函数,JS 也可以执行 WebAssembly export 的函数
  • 内存
    WebAssembly 使用的是线性内存,相当于 C 的堆。在 JavaScript 中,WebAssembly 的内存即一个 ArrayBuffer
    const memory = new WebAssembly.Memory({ initial: 1024, maximum: 2 * 1024 })
    console.log(memory.buffer instanceof ArrayBuffer) // true
    在 JavaScript 中,可以任意地读写这块内存:
    const uint8Array = new Uint8Array(memory.buffer)
    // 在 0xFF 写入 'A'
    uint8Array[0xFF] = 'A'.charCodeAt(0)
    console.log(uint8Array[0xFF]) // 65

Emscripten 生成的 .js 文件中针对 C/C++ 的特性封装了 WebAssembly API,提供了更方便的加载执行、函数调用、内存操作、类型转换等功能。

加载 WebAssembly

若要在我们自己的前端应用中加载 Emscripten 编译后的 WebAssembly,直接引入输出的 .js 文件即可。

这里我们添加一些参数:

docker run --rm -v $(pwd):/working trzeci/emscripten 
  emcc /working/main.c -o /working/cutils.js 
  -s MODULARIZE=1 
  -s EXPORT_NAME=CUtils

MODULARIZE=1 会使 Emscripten 以 UMD 模块格式输出 JavaScript,模块名为 EXPORT_NAME 所定义的名称。

若输出的是 .js 后缀,则不会生成 html 文件,仅生产 .js 和 .wasm

将 main.js 通过 script 标签、或所用打包工具的方式引用到前端:

<script src="path/to/cutils.js"></script>
<script>
  const Module = CUtils({
      onRuntimeInitialized: () => {
        // loaded
      },
  })
</script>

JavaScript 调用 C 函数

以计算文件的 MD5 为例。

我们使用这里的 MD5 代码,根据 md5.h 可以知道大致的使用方式为:

MD5_CTX md5_ctx;

MD5_Init(&md5_ctx);
MD5_Update(md5_ctx, buff, buff_size
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值