ChildProcess关闭,退出事件之间的区别

Nar*_*igo 27 event-handling child-process node.js

通过spawn()/exec()/...Node.js生成子进程时,子进程上有'close'一个'exit'事件和一个事件.

这两者之间有什么区别,什么时候需要使用什么?

Rob*_*b W 28

在Node.js 0.7.7之前,子进程上只有一个"退出"事件(没有"关闭"事件).子进程退出时将触发此事件,并关闭所有流(stdin,stdout,stdout).

在Node 0.7.7中,引入了"close"事件(请参阅commit).该文件(永久)目前表示:

当关闭子进程的stdio流时,将发出'close'事件.这与'exit'事件不同,因为多个进程可能共享相同的stdio流.

如果你只是生成一个程序并且没有对stdio做任何特殊操作,那么"关闭"事件会在"退出"之后触发.如果例如将stdout流传送到另一个流,则可以延迟"关闭"事件.这意味着"关闭"事件可以在"退出"事件之后(无限期地)延迟.
这是否意味着"退出"事件总是在"退出"之后被触发?如下面的例子所示,答案是否定的.

因此,如果您只对流程终止感兴趣(例如,因为流程拥有独占资源),那么监听"退出"就足够了.如果您不关心程序,只关心其输入和/或输出,请使用"关闭"事件.

实验:在杀死孩子之前消灭stdio

通过实验(在Node.js v7.2.0中),我发现如果子进程没有使用stdio流,那么只有在程序退出后才会触发"close"事件:

// 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);
Run Code Online (Sandbox Code Playgroud)

上述程序产生"睡眠"输出:

Closed all stdio
Going to kill
exited null SIGTERM
closed null SIGTERM
Run Code Online (Sandbox Code Playgroud)

当我将第一行更改为仅输出的程序时,

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

...然后输出是:

Closed all stdio
exited 1 null
closed 1 null
Going to kill
Run Code Online (Sandbox Code Playgroud)

类似地,当我更改spawn一个只从stdin读取的程序时,

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

或者当我从stdin读取并输出到stdout时,

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

实验:管道程序到另一个,杀死第一个程序

之前的实验非常人为.下一个实验更加现实:您将程序管道传输到另一个程序并终止第一个程序.

// 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's assume that it has started. Now kill it.
    cp.kill();
    console.log('Called kill()');
}, 500);
Run Code Online (Sandbox Code Playgroud)

输出:

Called kill()
exited null SIGTERM
closed null SIGTERM
Run Code Online (Sandbox Code Playgroud)

同样,当第一个程序只从输入读取而从不输出时:

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

当第一个程序在不等待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's 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);
Run Code Online (Sandbox Code Playgroud)

上面的程序输出以下内容然后退出:

Called kill()
exited null SIGTERM
Expecting "exit" to have fired, and not "close"
closed null SIGTERM
Run Code Online (Sandbox Code Playgroud)


小智 5

简短的版本是,当子进程退出但 stdio尚未关闭时,会发出“exit”。当子进程退出并且其 stdio 关闭时,会发出“close”。

除此之外,他们共享相同的签名。