WebAssembly(WASM)虚拟文件系统(FS)是一个用于在WebAssembly环境中模拟文件系统的工具。它允许开发者在浏览器或其他WebAssembly运行环境中使用类似于本地文件系统的功能。Emscripten提供了一个虚拟文件系统,以兼容libc/libcxx的同步文件访问函数。

Emscripten 虚拟文件系统架构

Emscripten 虚拟文件系统架构包括三种主要的文件系统:

  • MEMFS:MEMFS 内存文件系统,数据存储于内存中,页面刷新或程序重载后数据丢失
  • NODEFS:Node.js 文件系统,可以访问本地文件系统,适用于 Node.js 环境
  • IDBFS:IndexedDB 文件系统,基于浏览器的IndexedDB对象,可以持久化存储

这些文件系统通过 JavaScript 对象FS封装,供 fopen()、fread()、fwrite() 等 libc/libcxx 文件访问函数调用。

MEMFS

在使用MEMFS之前,需要将文件打包。可以使用emcc命令行或file_packager.py工具进行打包。以下是一个简单的示例:

// packfile.cc
int main() {
    FILE* fp = fopen("hello.txt", "rt");
    if (fp) {
        while (!feof(fp)) {
            char c = fgetc(fp);
            if (c != EOF) {
                putchar(c);
            }
        }
        fclose(fp);
    }
    return 0;
}

使用以下命令打包文件:

emcc packfile.cc -o packfile.js --preload-file hello.txt

NODEFS示例

NODEFS允许访问本地文件系统,以下是一个使用NODEFS的示例:

// nodefs.cc
void setup_nodefs() {
    EM_ASM(
        FS.mkdir('/data');
        FS.mount(NODEFS, {root:'.'}, '/data');
    );
}

int main() {
    setup_nodefs();
    FILE* fp = fopen("/data/nodefs_data.txt", "r+t");
    if (fp == NULL) fp = fopen("/data/nodefs_data.txt", "w+t");
    int count = 0;
    if (fp) {
        fscanf(fp, "%d", &count);
        count++;
        fseek(fp, 0, SEEK_SET);
        fprintf(fp, "%d", count);
        fclose(fp);
        printf("count:%d\n", count);
    } else {
        printf("fopen failed.\n");
    }
    return 0;
}

使用以下命令编译代码:

emcc nodefs.cc -o nodefs.js

IDBFS示例

IDBFS基于IndexedDB,可以持久化存储数据。以下是一个使用IDBFS的示例:

void sync_idbfs() {
    EM_ASM(
        FS.syncfs(function (err) {});
    );
}

EM_PORT_API(void) test() {
    FILE* fp = fopen("/data/nodefs_data.txt", "r+t");
    if (fp == NULL) fp = fopen("/data/nodefs_data.txt", "w+t");
    int count = 0;
    if (fp) {
        fscanf(fp, "%d", &count);
        count++;
        fseek(fp, 0, SEEK_SET);
        fprintf(fp, "%d", count);
        fclose(fp);
        printf("count:%d\n", count);
        sync_idbfs();
    } else {
        printf("fopen failed.\n");
    }
}

int main() {
    EM_ASM(
        FS.mkdir('/data');
        FS.mount(IDBFS, {}, '/data');
        FS.syncfs(true, function (err) {
            assert(!err);
            ccall('test', 'v');
        });
    );
    return 0;
}