Подтвердить что ты не робот

Разница между закрытием ChildProcess, события выхода

При рождении дочернего процесса через spawn()/exec()/... в Node.js на дочерних процессах есть событие 'close' и 'exit'.

В чем разница между этими двумя и когда вам нужно использовать что?

4b9b3361

Ответ 1

До Node.js 0.7.7 было только событие "выхода" для дочерних процессов (и никакого "закрытого" события). Это событие будет запущено, когда дочерний процесс завершится, и все потоки (stdin, stdout, stdout) были закрыты.

В Node 0.7.7 было введено событие "закрыть" (см. фиксацию). документация (постоянная ссылка) в настоящее время говорит:

Событие "закрыть" выдается, когда потоки stdio дочернего процесса закрыты. Это отличается от события "exit", поскольку несколько процессов могут совместно использовать одни и те же потоки stdio.

Если вы просто создаете программу и не делаете ничего особенного с stdio, событие "закрыть" запускается после "выхода". Событие "закрыть" может быть отложено, если, например, поток stdout передается по каналу в другой поток. Таким образом, это означает, что событие "закрыть" может быть отложено (неопределенно) после события "выхода".
Означает ли это, что событие "закрыть" всегда запускается после "выхода"? Как показывают нижеприведенные примеры, ответ отрицательный.

Итак, если вас интересует только прекращение процесса (например, поскольку процесс содержит эксклюзивный ресурс), достаточно прослушать "exit". Если вы не заботитесь о программе и только о ее вводе и/или выходе, используйте событие "закрыть" .

Эксперимент: уничтожить stdio перед смертью ребенка

Экспериментально (в Node.js v7.2.0) я обнаружил, что если потоки stdio не используются дочерним процессом, то событие "закрыть" запускается только после выхода программы:

// The "sleep" command takes no input and gives no output.
cp = require('child_process').spawn('sleep', ['100']);
cp.on('exit', console.log.bind(console, 'exited'));
cp.on('close', console.log.bind(console, 'closed'));
cp.stdin.end();
cp.stdout.destroy();
cp.stderr.destroy();
console.log('Closed all stdio');
setTimeout(function() { 
    console.log('Going to kill');
    cp.kill();
}, 500);

Вышеупомянутая программа генерирует выходы "sleep":

Closed all stdio
Going to kill
exited null SIGTERM
closed null SIGTERM

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

// The "yes" command continuously outputs lines with "y"
cp = require('child_process').spawn('yes');

... тогда выход:

Closed all stdio
exited 1 null
closed 1 null
Going to kill

Аналогично, когда я изменяю порождающую программу, которая только читает из stdin,

// Keeps reading from stdin.
cp = require('child_process').spawn('node', ['-e', 'process.stdin.resume()']);

Или когда я читаю из stdin и выводя на stdout,

// "cat" without arguments reads from stdin, and outputs to stdout
cp = require('child_process').spawn('cat');

Эксперимент: программа труб другому, убейте первую программу

Предыдущий эксперимент довольно искусственный. Следующий эксперимент немного реалистичен: вы передаете программу другому и убиваете первый.

// Reads from stdin, output the input to stdout, repeat.
cp = require('child_process').spawn('bash', ['-c', 'while read x ; do echo "$x" ; done']);
cp.on('exit', console.log.bind(console, 'exited'));
cp.on('close', console.log.bind(console, 'closed'));

cpNext = require('child_process').spawn('cat');
cp.stdout.pipe(cpNext.stdin);

setTimeout(function() {
    // Let assume that it has started. Now kill it.
    cp.kill();
    console.log('Called kill()');
}, 500);

Вывод:

Called kill()
exited null SIGTERM
closed null SIGTERM

Аналогично, когда первая программа считывает только с ввода и никогда не выдает:

// Keeps reading from stdin, never outputs.
cp = require('child_process').spawn('bash', ['-c', 'while read ; do : ; done']);

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

Эксперимент: Трубная программа с большим количеством вывода на другой, убить первую программу

// Equivalent to "yes | cat".
cp = require('child_process').spawn('yes');
cp.on('exit', console.log.bind(console, 'exited'));
cp.on('close', console.log.bind(console, 'closed'));

cpNext = require('child_process').spawn('cat');
cp.stdout.pipe(cpNext.stdin);

setTimeout(function() {
    // Let assume that it has started. Now kill it.
    cp.kill();
    console.log('Called kill()');
    setTimeout(function() {
        console.log('Expecting "exit" to have fired, and not "close"');
        // cpNext.kill();
        // ^ Triggers 'error' event, errno ECONNRESET.
        // ^ and does not fire the 'close' event!

        // cp.stdout.unpipe(cpNext.stdin);
        // ^ Does not appear to have any effect.
        // ^ calling cpNext.kill() throws ECONNRESET.
        // ^ and does not fire the 'close' event!

        cp.stdout.destroy(); // <-- triggers 'close'
        cpNext.stdin.destroy();
        // ^ Without this, cpNext.kill() throws ECONNRESET.

        cpNext.kill();
    }, 500);
}, 500);

Вышеуказанная программа выводит следующие и затем выходит:

Called kill()
exited null SIGTERM
Expecting "exit" to have fired, and not "close"
closed null SIGTERM

Ответ 2

Вы посмотрели документацию?

В соответствии с this:

Событие "закрыть" выдается, когда потоки stdio дочернего процесса закрыты. Это отличается от события 'exit', поскольку несколько процессов могут совместно использовать те же потоки stdio.

Событие 'exit' выдается после завершения дочернего процесса. Если процесс завершен, код является окончательным кодом завершения процесса, в противном случае - нулем. Если процесс прекращается из-за приема сигнала, сигнал является строковым именем сигнала, в противном случае - нулевым. Один из двух всегда будет не нулевым.

Ответ 3

короткая версия, "exit" испускается, когда ребенок выходит, но stdio еще не закрыт. "close" испускается, когда ребенок вышел из и, его stdios закрыты.

Кроме того, они имеют одну и ту же подпись.