Node.js fs.open () зависает после попытки открыть более 4 именованных каналов (FIFO) - PullRequest
0 голосов
/ 02 октября 2018

У меня есть процесс node.js, который должен читать из нескольких именованных каналов, передаваемых разными другими процессами, в качестве метода IPC.

Я понял, что после открытия и создания потоков чтения из более чем четырех ярусов, что fsКажется, больше не сможет открыть fifos и просто висит там.

Кажется, что это число немного мало, учитывая, что можно без проблем открыть тысячи файлов одновременно (например, заменив mkfifo на touch в следующем скрипте).

Я тестировал с node.js v10.1.0 на MacOS 10.13 и с node.js v8.9.3 на Ubuntu 16.04 с тем же результатом.


Неисправный скрипт

И скрипт, который отображает это поведение:

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

Repl.it link


ПРИМЕЧАНИЕ. Следующая часть этого вопроса связана с тем, что я уже пытался ответить на вопрос, но, возможно, не является центральным для него


Два интересных факта с этим сценарием

  • при записи дважды в один из FIFO (т. Е. echo hello > fifo) узел затем может открыть еще один fifo, но больше не получает оттот, в котором мы написали
  • , когда поток чтения создается непосредственно провидиномЕсли указать путь к fifo (вместо fd), скрипт больше не блокируется, но, по-видимому, больше не получает то, что написано в любом из FIFO

Debugинформация

Затем я попытался проверить, может ли это быть связано с каким-либо ограничением ОС, например, количеством открытых файловых дескрипторов.

Выход 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 ++ скрипт успешно открывает сто пятнадцать.

Обратите внимание, что между этими двумя реализациями есть несколько отличий.В C ++ one,

  • скрипт только открывает 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;
}

В качестве примечания, перед выполнением сценария необходимо создать пятерки, например, с помощью

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


Потенциальная проблема, связанная с Node.js

После небольшого поиска, кажется, это также связано с этой проблемой на Gitub Node.js:

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

Но люди, похоже, жалуются на противоположное поведение (fs.open () выдает ошибки EMFILE и не зависает молча ...)


Как видите, япытался искать во многих направлениях, и все это привело меня к моему вопросу:

Знаете ли вы, что может вызвать такое поведение?

Спасибо

1 Ответ

0 голосов
/ 03 октября 2018

Итак, я задал вопрос на Node.js Github, https://github.com/nodejs/node/issues/23220

Из решения:

Работа с FIFO в настоящее время немногоtricky.

Системный вызов 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.
});
...