我有一个 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 错误而不是默默地挂起......)
正如你所看到的,我尝试从多个方向进行搜索,所有这些都引出了我的问题:
您知道什么可能导致这种行为吗?
谢谢