我有一个node.js进程,需要从多个命名管道中读取数据,这些管道由其他不同的进程作为IPC方法提供。
当我打开并创建了四个及以上的管道读取流之后,发现fs似乎无法再打开管道,并且停在那里不动。
考虑到可以同时打开数千个文件而不会出现问题(例如通过将mkfifo
替换为以下脚本中的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
注意:下面的问题与我已经尝试回答该问题所涉及的内容有关,但可能不是核心问题。
该脚本有两个有趣的事实:
- 当在其中一个FIFO中写两次时(例如
echo hello > fifo
),Node就能够打开另一个FIFO,但不再从我们写入的那个FIFO接收数据。 - 当读取流通过直接提供FIFO路径(而不是fd)创建时,脚本不再被阻塞,但显然也不再接收任何一个FIFO中的写入内容。
调试信息
我尝试验证是否与某些操作系统限制有关,例如打开的文件描述符数量。
在Mac上运行ulimit -a
的输出为
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。但人们似乎抱怨相反的行为(fs.open()抛出EMFILE错误而不是静默挂起...)正如您所看到的,我尝试在许多方向上进行搜索,所有这些都导致了我的问题:
您知道是什么原因导致这种行为吗?
谢谢