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

13

存在适用于node.js的mmap模块:https://github.com/bnoordhuis/node-mmap/

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

那么,我想知道如何设计一个非阻塞式的内存映射模块来适用于node.js?线程,纤程等技术可行吗?

显然,这也引发了一个问题,即在node.js中使用线程是否只会发生在请求处理程序之外。

1个回答

19

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

如果我们快速查看node-mmap源码,我们会发现它其实非常简单。它调用mmap并返回一个封装了映射内存区域的node Buffer

从这个Buffer读取数据会导致操作系统执行I/O。由于这必须在JS线程上发生,因此我们通过磁盘I/O阻塞了JS线程。

不要返回一个Buffer,使JS直接访问映射内存,而应该编写一个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。然后工作者传递指向副本的指针,C回调将其包装在一个Buffer中返回给JS-land。

您还可以尝试在范围内进行读取(以便任何必要的I/O发生在工作者线程上),但实际上不对数据进行任何处理,然后让C回调简单地将映射的内存范围包装在Buffer中。理论上,工作者读取的文件部分将留在RAM中,因此对该部分映射内存的访问不会阻塞。但是,我对映射内存的了解不足,无法确定这是否会带来问题。


最后,我对这是否会比节点的常规fs方法提供任何额外的性能持怀疑态度。只有在我正在进行确实需要使用mmap的工作时,才会采用这种方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接