Node.js fs.open() 在尝试打开超过 4 个命名管道 (FIFO) 后挂起

2024-03-23

我有一个 node.js 进程,需要从不同其他进程提供的多个命名管道中读取数据作为 IPC 方法。

我意识到在打开并创建来自四个以上 fifo 的读取流后, fs 似乎不再能够打开 fifo 并只是挂在那里。

考虑到可以毫无问题地同时打开数千个文件(例如通过替换mkfifo by touch在以下脚本中)。

我在 MacOS 10.13 上使用 node.js v10.1.0 进行测试,在 Ubuntu 16.04 上使用 node.js v8.9.3 进行测试,结果相同。


有问题的脚本

以及显示此行为的脚本:

var fs = require("fs");
var net = require("net");
var child_process = require('child_process');

var uuid = function() {
    for (var i = 0, str = ""; i < 32; i++) {
        var number = Math.floor(Math.random() * 16);
        str += number.toString(16);
    }
    return str;
}

function setupNamedPipe(cb) {
    var id = uuid();
    var fifoPath = "/tmp/tmpfifo/" + id;

    child_process.exec("mkfifo " + fifoPath, function(error, stdout, stderr) {
        if (error) {
            return;
        }

        fs.open(fifoPath, 'r+', function(error, fd) {
            if (error) {
                return;
            }

            var stream = fs.createReadStream(null, {
                fd
            });
            stream.on('data', function(data) {
                console.log("FIFO data", data.toString());
            });
            stream.on("close", function(){
                console.log("close");
            });
            stream.on("error", function(error){
                console.log("error", error);
            });

            console.log("OK");
            cb();
        });
    });
}

var i = 0;
function loop() {
    ++i;
    console.log("Open ", i);
    setupNamedPipe(loop);
}

child_process.exec("mkdir -p /tmp/tmpfifo/", function(error, stdout, stderr) {
    if (error) {
        return;
    }

    loop();
});

这个剧本在他身后并没有清理干净,别忘了rm -r /tmp/tmpfifo

复制链接 https://repl.it/repls/DarkorchidSpatialFacts


注意,这个问题的以下部分与我已经尝试回答的问题相关,但可能不是它的核心


这个脚本有两个有趣的事实

  • 当在其中一个 FIFO 中写入两次时,(即echo hello > fifo) 然后节点就能够再打开一个 fifo,但不再从我们写入的那个 fifo 接收数据
  • 当通过直接提供 fifo(而不是 fd)的路径来创建读取流时,脚本不再阻塞,但显然不再接收任何 FIFO 中写入的内容

调试信息

然后我尝试验证这是否与某些操作系统限制有关,例如打开的文件描述符的数量。

输出ulimit -a在 Mac 上是

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
file size               (blocks, -f) unlimited
max locked memory       (kbytes, -l) unlimited
max memory size         (kbytes, -m) unlimited
open files                      (-n) 256
pipe size            (512 bytes, -p) 1
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 1418
virtual memory          (kbytes, -v) unlimited

没有任何迹象表明 4 存在某种限制。


C++暂定

然后我尝试用 C++ 编写一个类似的脚本。 在 C++ 中,脚本成功打开一百个 fifo。

请注意,这两种实现之间存在一些差异。在 C++ 中,

  • 该脚本仅打开 fifo,
  • 没有尝试去阅读,
  • 并且没有多线程
#include <string>
#include <cstring>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>

int main(int argc, char** argv)
{

    for (int i=0; i < 100; i++){
        std::string filePath = "/tmp/tmpfifo/" + std::to_string(i);
        auto hehe = open(filePath.c_str(), O_RDWR);
        std::cout << filePath << " " << hehe << std::endl;
    }

    return 0;
}

附带说明一下,需要在执行脚本之前创建 fifo,例如使用

for i in $(seq 0 100); do mkfifo /tmp/tmpfifo/$i; done


潜在的 Node.js 相关问题

经过一番搜索后,它似乎也与 Node.js Github 上的该问题相关:

https://github.com/nodejs/node/issues/1941 https://github.com/nodejs/node/issues/1941.

但人们似乎在抱怨相反的行为(fs.open() 抛出 EMFILE 错误而不是默默地挂起......)


正如你所看到的,我尝试从多个方向进行搜索,所有这些都引出了我的问题:

您知道什么可能导致这种行为吗?

谢谢


所以我在 Node.js Github 上问了这个问题,https://github.com/nodejs/node/issues/23220 https://github.com/nodejs/node/issues/23220

从解决方案来看:

目前处理 FIFO 有点棘手。

The open()默认情况下,系统调用会阻塞在 FIFO 上,直到管道的另一端也被打开。由于 Node.js 使用线程池进行文件系统操作,因此打开多个管道open()调用未完成会耗尽该线程池。

解决方案是以非阻塞模式打开文件,但是这样做的困难在于其他方式fs调用不是在考虑非阻塞文件描述符的情况下构建的;net.Socket然而。

所以,解决方案看起来像这样:

fs.open('path/to/fifo/', fs.constants.O_RDONLY | fs.constants.O_NONBLOCK, (err, fd) => {
  // Handle err
  const pipe = new net.Socket({ fd });
  // Now `pipe` is a stream that can be used for reading from the FIFO.
});
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Node.js fs.open() 在尝试打开超过 4 个命名管道 (FIFO) 后挂起 的相关文章

随机推荐