我有一个node.js进程,需要从其他进程作为IPC方法馈送的多个命名管道中进行读取。
我意识到打开并创建来自四个以上fifos的读取流后,fs似乎不再能够打开fifos并只能挂在那里。
考虑到可以同时打开数千个文件而没有麻烦(例如,在以下脚本中将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
注意,该问题的以下部分与我已经尝试回答的问题有关,但可能并非该问题的重点
此脚本有两个有趣的事实
echo hello > fifo
)时,节点便可以再打开一个fifo,但不再从我们写入的那个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 ++中,
#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错误并且没有默默挂起...)
如您所见,我试图在多个方向进行搜索,所有这些使我想到了我的问题:
您知道什么可能导致此行为吗?
谢谢
答案 0 :(得分:2)
所以我在Node.js Github上问了一个问题,https://github.com/nodejs/node/issues/23220
从解决方案中:
处理FIFO当前有些棘手。
默认情况下,
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. });