我如何设计和实现一个非阻塞内存映射模块的node.js



node.js中存在mmap模块:https://github.com/bnoordhuis/node-mmap/

正如作者Ben Noordhuis所指出的,访问映射内存可能会阻塞,这就是为什么他不再推荐它并停止使用它的原因。

所以我想知道如何为node.js设计一个非阻塞内存映射模块?穿线,纤维,?

显然,这附近提出了一个问题,如果线程在node.js只是发生在其他地方,而不是请求处理程序。

当谈到以非阻塞方式实现某些本机功能时,首先要看的是libuv。它是node的核心模块与底层平台的接口。特别有趣的是工作队列API。

如果我们快速查看一下node-mmap的源代码,我们会发现它实际上非常简单。它调用mmap并返回一个包含映射内存区域的节点Buffer

从这个Buffer读取是导致操作系统执行I/O的原因。因为这必然会发生在JS线程上,我们最终会用磁盘I/o阻塞JS线程。

不是返回允许JS直接访问映射内存的Buffer,而是应该用c++编写一个包装器类,通过工作队列封送读取和写入。通过这种方式,磁盘I/O将发生在一个单独的线程上。

在JS中,你可以这样使用它:

fs.open('/path/to/file', 'r', function(err, fd) {
    fs.fstat(fd, function(err, stats) {
        var mapped = mmap.map(stats.size, mmap.PROT_READ, mmap.MAP_SHARED, fd, 0);
        mapped.read(start, len, function(err, data) {
            // ...
        });
    });
});

在C语言中,read函数将创建libuv工作请求并将其放入工作队列中。然后,C工作函数将读取映射的内存范围(基于调用者的规范),这可能会导致磁盘I/O,但这是安全的,因为它发生在单独的线程上。

接下来发生的事情很有趣。安全的方法是工作线程从映射的内存中alloc一个新的内存块和memcpy。然后worker传递一个指向副本的指针,C回调将其封装在一个Buffer中返回给JS-land。

您也可以尝试读取范围(以便在工作线程上发生任何必要的I/O),但实际上不对数据做任何事情,然后让C回调简单地将映射的内存范围包装在Buffer中。理论上,工作程序读取的文件部分将留在RAM中,因此对映射内存部分的访问不会阻塞。然而,老实说,我对映射内存的了解还不够多,无法判断这是否会让你吃苦头。


最后,我怀疑这是否会比node的常规fs方法提供任何额外的性能。我只会在需要使用mmap的时候才会这么做。

最新更新