【问题标题】:How to access WebAssembly linear memory from C/C++如何从 C/C++ 访问 WebAssembly 线性内存
【发布时间】:2018-03-26 16:35:25
【问题描述】:

我正在编写一个小型 C 程序,旨在通过 emcc 编译为 wasm 并在 Web 浏览器中运行。因为 wasm 导出的函数只能接受简单的数值作为参数输入和返回值,所以我需要在 JavaScript API 和编译的 WebAssembly 代码之间共享内存,以便访问更复杂的数据类型,如字符串或 char 数组。问题是我一辈子都不知道如何从我的 C 程序内部访问WebAssembly linear memory

我的最终目标是能够在我的 C 程序中读取在 JavaScript 中初始化的字符串,然后还可以在 Web 浏览器的 JavaScript 代码中读取在我的 C 程序中修改/初始化的字符串。

这是我正在尝试做的一个基本示例:

main.js

const importObject = {
  'env': {
    'memoryBase': 0,
    'tableBase': 0,
    'memory': new WebAssembly.Memory({initial: 256}),
    'table': new WebAssembly.Table({initial: 0, element: 'anyfunc'})
  }
}

// using the fetchAndInstantiate util function from
// https://github.com/mdn/webassembly-examples/blob/master/wasm-utils.js
fetchAndInstantiate('example.wasm', importObject).then(instance => {

      // call the compiled webassembly main function
      instance.exports._main()
      console.log(importObject.env.memory)
})

example.c

int main() {
    // somehow access importObject.env.memory 
    // so that I can write a string to it
    return 0;
}

This question 让我了解了其中的一部分,但是,我仍然不明白如何在我的 C 代码中从 WebAssembly 内存缓冲区读取/写入。

【问题讨论】:

    标签: emscripten webassembly


    【解决方案1】:

    您需要做的是在 WebAssembly 模块中传达 C 和 JavaScript 代码都可以读取/写入的位置。

    这是一个简单的例子,它为数组中的每个元素添加一个数字。这是C代码:

    const int SIZE = 10;
    int data[SIZE];
    
    void add(int value) { 
      for (int i=0; i<SIZE; i++) {
        data[i] = data[i] + value;
      }
    }
    
    int* getData() {
      return &data[0];
    }
    

    上面代码中重要的是int* getData()函数,它返回一个对data数组开头的引用。当编译到 WebAssembly 时,这将返回一个整数,它是模块线性内存中 data 数组的位置。

    这是一个如何使用它的示例:

    var wasmModule = new WebAssembly.Module(wasmCode);
    var wasmInstance = new WebAssembly.Instance(wasmModule, wasmImports);
    
    // obtain the offset to the array
    var offset = wasmInstance.exports.getData();
    
    // create a view on the memory that points to this array
    var linearMemory = new Uint32Array(wasmInstance.exports.memory.buffer, offset, 10);
    
    // populate with some data
    for (var i = 0; i < linearMemory.length; i++) {
      linearMemory[i] = i;
    }
    
    // mutate the array within the WebAssembly module
    wasmInstance.exports.add(10);
    
    // log the results
    for (var i = 0; i < linearMemory.length; i++) {
      log(linearMemory[i]);
    }
    

    您可以在WASM fiddle 中查看完整示例。

    【讨论】:

    • 感谢伟大的教育例子!我试图让它使用 C++ 向量进行存储,但遇到了一些错误。可以请有人尝试使这个示例与 WasmFiddle 中的 std::vector 一起使用吗? :stackoverflow.com/a/51456618/544721
    【解决方案2】:

    有两种相反的方法:

    1. 将所有数据元素声明为全局变量,并添加帮助函数以返回每个数据元素的起始地址。
    2. 不要使用全局变量,在 JS 中分配所需的内存,计算偏移量并将这些偏移量传递给调用的函数。在这种情况下,可用内存将从 0(零)开始。

    (1) 适用于简单的事情。 (2) 适用于您的数据大小未知的情况。

    【讨论】:

    • 我建议在 thie repo github.com/nodeca/multimath 中挖掘代码。它使用(2)方法。
    • 深入研究代码,但没有看到我尚未尝试过的内容。我认为 API 仍然不稳定。
    【解决方案3】:

    WASM 对象有一个属性(我还没有看到文档),它存储了一个指向每个变量和数组开头的指针。

    鉴于这个 C:

    int myArray[100];
    
    int main(){
        // Fill the array with data so we can see it
        for(int i = 0; i < 100; i ++){
            myArray[i] = 100 - i;
        }
        
        return 1;
    }
    

    您可以像这样访问数组的完整数据:

    // Load WASM
    fetch('script.wasm',{headers:{'Content-Type':'application/wasm'}})
    .then(response => response.arrayBuffer())
    .then(bits => WebAssembly.instantiate(bits))
    .then(obj => {
        // We pull back 
        var sharedArray = new Int32Array(
            obj.instance.exports.memory.buffer, // WASM's memory
            obj.instance.exports.myArray.value, // myArray's pointer
            100                                 // The array's length
        );
    
        obj.instance.exports.main();
    
        console.log(sharedArray);
    });
    

    在 Chromium 和 Firefox 中测试。您不必为此使用 Emscripten。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多