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

cit*_*kid 7 mmap memory-mapped-files memory-mapping node.js

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

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

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

显然,如果node.js中的线程只发生在其他地方而不是请求处理程序,那么这附近会引发一个问题.

jos*_*736 12

当谈论以非阻塞方式实现一些本地设施时,首先要看的是libuv.这是节点的核心模块与底层平台的接口.特别感兴趣的是工作队列 API.

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

从中读取Buffer是导致操作系统执行I/O的原因.因为这必然会在JS线程上发生,我们最终会阻止带有磁盘I/O的JS线程.

Buffer您应该在C++中编写一个包装类,通过工作队列进行读写操作,而不是返回允许JS直接访问映射内存的方法.这样,磁盘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) {
            // ...
        });
    });
});
Run Code Online (Sandbox Code Playgroud)

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

接下来发生的事情很有趣.安全的方法是为工作者alloc提供新的内存块和memcpy映射的内存.然后工作者将指针传递给副本,然后C回调将其包装在一个Buffer要返回给JS-land的地方.

您还可以尝试读取范围(以便在工作线程上发生任何必要的I/O)但实际上没有对数据执行任何操作,然后使用C回调只是将映射的内存范围包装在a中Buffer.从理论上讲,工作者读取的文件部分将保留在RAM中,因此访问映射内存的那部分不会阻塞.但是,老实说我对映射内存知之甚少,不知道这是否会最终让你感到困惑.


最后,我怀疑这是否会在节点的常规fs方法上提供任何额外的性能.如果我做的事情非常合理,mmap我只会走这条路.