子进程
child_process
模块提供了类似 popen(3)
的处理三向数据流(stdin/stdout/stderr)的功能。
它能够以完全非阻塞的方式与子进程的 stdin
、stdout
和 stderr
以流式传递数据。(请注意,某些程序在内部使用行缓冲
I/O。这不会影响到 node.js,但您发送到子进程的数据不会被立即消费。)
require(‘child_process‘).spawn()
或者 require(‘child_process‘).fork()
创建子进程,这两种方法的语义有些区别,下文将会解释。child.stdin
、child.stdout
和 child.stderr
。它们可以共享父进程的
stdio 流,也可以作为独立的被导流的流对象。
事件: ‘error‘
-
err
{Error Object} 错误。
事件: ‘exit‘
-
code
{Number} 假如进程正常退出,则为它的退出代码。 -
signal
{String} 假如是被父进程终止,则为所传入的终止子进程的信号。
signal
就代表着信号的名称,
否则为null
.
waitpid(2)
.事件: ‘close‘
-
code
{Number} 假如进程正常退出,则为它的退出代码。 -
signal
{String} 假如是被父进程终止,则为所传入的终止子进程的信号。
事件: ‘disconnect‘
在子进程或父进程中使用使用.disconnect()方法后,这个事件会被触发,在断开之后,就不可能再相互发送信息了。可以通过检查子进程的child.connected属性是否为true去检查是否可以发送信息事件: ‘message‘
-
message
{Object} 一个已解析的JSON对象或者原始类型值 -
sendHandle
{Handle object} 一个socket 或者 server对象
child.stdin
- {Stream object}
child.stdout
- {Stream object}
stdout
是个可读流。child.stderr
- {Stream object}
child.pid
- {Integer}
var spawn = require(‘child_process‘).spawn,
grep = spawn(‘grep‘, [‘ssh‘]);
console.log(‘Spawned child pid: ‘ + grep.pid);
grep.stdin.end();
child.kill([signal])
-
signal
{String}
‘SIGTERM‘
.
参阅 signal(7)
查看所有可用的signals列表var spawn = require(‘child_process‘).spawn,
grep = spawn(‘grep‘, [‘ssh‘]);
grep.on(‘close‘, function (code, signal) {
console.log(‘child process terminated due to receipt of signal ‘+signal);
});
// 将SIGHUP发送给进程
grep.kill(‘SIGHUP‘);
当一个signal不能被传递的时候,会触发一个‘error‘事件, 发送一个信号到已终止的子线程不会发生错误,但是可能引起不可预见的后果, 假如该子进程的ID已经重新分配给了其他进程,signal将会被发送到其他进程上面,大家可以猜想到这发生什么后果。kill(2)
child.connected
- 方法`.disconnect‘ 被调用之后,Boolean 将被设置为false。
若 .connected
为flase,将不再发送消息。
child.send(message, [sendHandle])
-
message
{Object} -
sendHandle
{Handle object}
child_process.fork()
你可以使用 child.send(message,
[sendHandle])
向子进程写数据 and 数据将通过子进程上的‘message’事件接受。
var cp = require(‘child_process‘);
var n = cp.fork(__dirname + ‘/sub.js‘);
n.on(‘message‘, function(m) {
console.log(‘PARENT got message:‘, m);
});
n.send({ hello: ‘world‘ });
然后是子进程脚本的代码, ‘sub.js‘
代码如下:process.on(‘message‘, function(m) {
console.log(‘CHILD got message:‘, m);
});
process.send({ foo: ‘bar‘ });
在子进程脚本中‘process‘对象有‘send()’方法, ‘process’每次通过它的信道接收到信息都会触发事件,信息以对象形式返回。{cmd:
‘NODE_foo‘}
信息是个比较特殊的情况. 所有在‘cmd’属性中包含 a NODE_
前缀的信息将不会触发‘message’事件,
因为他们是由node 核心使用的内部信息. 相反这种信息会触发 internalMessage
事件,
你应该通过各种方法避免使用这种特性, 他改变的时候不会接收到通知。child.send()
的sendHandle
选项是用来发送一个TCP服务或者socket对象到另一个线程的,子进程将会接收这个参数作为‘message’事件的第二个参数。示例: 发送一个server对象
这是一个发送一个server对象的示例:var child = require(‘child_process‘).fork(‘child.js‘);
// 开启一个server对象,并发送句柄。
var server = require(‘net‘).createServer();
server.on(‘connection‘, function (socket) {
socket.end(‘handled by parent‘);
});
server.listen(1337, function() {
child.send(‘server‘, server);
});
process.on(‘message‘, function(m, server) {
if (m === ‘server‘) {
server.on(‘connection‘, function (socket) {
socket.end(‘handled by child‘);
});
}
});
注意,server对象现在有父进程和子进程共享,这意味着某些连接将会被父进程和子进程处理。示例: 发送socket对象
这是个发送socket的例子. 他将创建两个子线程 ,同时处理连接,这是通过使用远程地址74.125.127.100
作为
VIP 发送socket到一个‘特殊’的子线程. 其他的socket将会发送到‘正常’的线程里。var normal = require(‘child_process‘).fork(‘child.js‘, [‘normal‘]);
var special = require(‘child_process‘).fork(‘child.js‘, [‘special‘]);
// 开启service同时将套接字发送给子进程
var server = require(‘net‘).createServer();
server.on(‘connection‘, function (socket) {
// 若是VIP
if (socket.remoteAddress === ‘74.125.127.100‘) {
special.send(‘socket‘, socket);
return;
}
// just the usual dudes
normal.send(‘socket‘, socket);
});
server.listen(1337);
child.js
文件代码如下:process.on(‘message‘, function(m, socket) {
if (m === ‘socket‘) {
socket.end(‘You were handled as a ‘ + process.argv[2] + ‘ person‘);
}
});
注意,一旦单个的socket被发送到子进程,当这个socket被删除之后,父进程将不再对它保存跟踪,这表明了这个条件下‘.connetions’属性将变成‘null‘, 在这个条件下同时也不推荐时间‘.maxConnections’。child.disconnect()
使用child.disconnect()
方法关闭父进程与子进程的IPC连接.
他让子进程非常优雅的退出,因为已经没有活跃的IPC信道. 当调用这个方法,‘disconnect’事件将会同时在父进程和子进程内被触发,‘connected’的标签将会被设置成‘flase’, 请注意,你也可以在子进程中调用‘process.disconnect()’‘disconnect‘ 事件是当进程没有收到通知时最有可能立即发送通知的方法。
注意:你也可以在子进程与父进程有IPC连接时回调process.disconnect()
(例如fork()
)。
child_process.spawn(command, [args], [options])
-
command
{String}要运行的命令 -
args
{Array} 字符串参数列表 -
options
{Object}-
cwd
{String} 子进程的当前的工作目录 -
stdio
{Array|String} 子进程 stdio 配置. (参阅下文) -
customFds
{Array} Deprecated 作为子进程 stdio 使用的 文件标示符. (参阅下文) -
env
{Object} 环境变量的键值对 -
detached
{Boolean} 子进程将会变成一个进程组的领导者. (参阅下文) -
uid
{Number} 设置用户进程的ID. (See setuid(2).) -
gid
{Number} 设置进程组的ID. (See setgid(2).)
-
- 返回: {ChildProcess object}
{ cwd: undefined,
env: process.env
}
cwd
允许你从被创建的子进程中指定一个工作目录.
使用 env
去指定在新进程中可用的环境变量。ls
-lh /usr
的示例, 获取stdout
, stderr
,
和退出代码:var spawn = require(‘child_process‘).spawn,
ls = spawn(‘ls‘, [‘-lh‘, ‘/usr‘]);
ls.stdout.on(‘data‘, function (data) {
console.log(‘stdout: ‘ + data);
});
ls.stderr.on(‘data‘, function (data) {
console.log(‘stderr: ‘ + data);
});
ls.on(‘close‘, function (code) {
console.log(‘child process exited with code ‘ + code);
});
示例: 一个非常精巧的方法执行 ‘ps ax | grep ssh‘
var spawn = require(‘child_process‘).spawn,
ps = spawn(‘ps‘, [‘ax‘]),
grep = spawn(‘grep‘, [‘ssh‘]);
ps.stdout.on(‘data‘, function (data) {
grep.stdin.write(data);
});
ps.stderr.on(‘data‘, function (data) {
console.log(‘ps stderr: ‘ + data);
});
ps.on(‘close‘, function (code) {
if (code !== 0) {
console.log(‘ps process exited with code ‘ + code);
}
grep.stdin.end();
});
grep.stdout.on(‘data‘, function (data) {
console.log(‘‘ + data);
});
grep.stderr.on(‘data‘, function (data) {
console.log(‘grep stderr: ‘ + data);
});
grep.on(‘close‘, function (code) {
if (code !== 0) {
console.log(‘grep process exited with code ‘ + code);
}
});
检查执行错误的例子:var spawn = require(‘child_process‘).spawn,
child = spawn(‘bad_command‘);
child.stderr.setEncoding(‘utf8‘);
child.stderr.on(‘data‘, function (data) {
if (/^execvp\(\)/.test(data)) {
console.log(‘Failed to start child process.‘);
}
});
注意,当在spawn过程中接收一个空对象,这会导致创建的进程使用空的环境变量而不是使用‘process.env’.这是由于与一个废弃API向后兼容的问题。child_process.spawn()
中的 stdio
选项是一个数组,每个索引对应子进程中的一个文件标识符。可以是下列值之一:-
‘pipe‘
-在子进程与父进程之间创建一个管道,管道的父进程端以child_process
的属性的形式暴露给父进程,如ChildProcess.stdio[fd]
。 为 文件标识(fds) 0 - 2 建立的管道也可以通过 ChildProcess.stdin,ChildProcess.stdout 及 ChildProcess.stderr 分别访问。 -
‘ipc‘
- 创建一个IPC通道以在父进程与子进程之间传递 消息/文件标识符。一个子进程只能有最多一个IPC stdio 文件标识。 设置该选项激活 ChildProcess.send() 方法。如果子进程向此文件标识符写JSON消息,则会触发 ChildProcess.on("message")。 如果子进程是一个nodejs程序,那么IPC通道的存在会激活process.send()和process.on(‘message‘) -
‘ignore‘
- 不在子进程中设置该文件标识。注意,Node 总是会为其spawn的进程打开 文件标识(fd) 0 - 2。 当其中任意一项被 ignored,node 会打开/dev/null
并将其附给子进程的文件标识(fd)。 -
Stream
对象 - 与子进程共享一个与tty,文件,socket,或者管道(pipe)相关的可读或可写流。 该流底层(underlying)的文件标识在子进程中被复制给stdio数组索引对应的文件标识(fd) -
正数 - 该整形值被解释为父进程中打开的文件标识符。他与子进程共享,和
Stream
被共享的方式相似。 -
null
,undefined
- 使用默认值。 对于stdio fds 0,1,2(或者说stdin,stdout和stderr),pipe管道被建立。对于fd 3及往后,默认为ignore
stdio
参数除了数组也可以是下列字符串之一:-
ignore
-[‘ignore‘, ‘ignore‘, ‘ignore‘]
-
pipe
-[‘pipe‘, ‘pipe‘, ‘pipe‘]
-
inherit
-[process.stdin, process.stdout, process.stderr]
or[0,1,2]
var spawn = require(‘child_process‘).spawn;
// Child will use parent‘s stdios
spawn(‘prg‘, [], { stdio: ‘inherit‘ });
// Spawn child sharing only stderr
spawn(‘prg‘, [], { stdio: [‘pipe‘, ‘pipe‘, process.stderr] });
// Open an extra fd=4, to interact with programs present a
// startd-style interface.
spawn(‘prg‘, [], { stdio: [‘pipe‘, null, null, null, ‘pipe‘] });
如果 detached
选项被设置,则子进程会被作为新进程组的
leader。这使得子进程可以在父进程退出后继续运行。child
,使用child.unref()
方法,则父进程的事件循环引用计数中将不会包含这个子进程。 var fs = require(‘fs‘),
spawn = require(‘child_process‘).spawn,
out = fs.openSync(‘./out.log‘, ‘a‘),
err = fs.openSync(‘./out.log‘, ‘a‘);
var child = spawn(‘prg‘, [], {
detached: true,
stdio: [ ‘ignore‘, out, err ]
});
child.unref();
当使用 detached
选项来启动一个长时间运行的进程,该进程不会在后台保持运行,除非向它提供了一个不连接到父进程的 stdio
配置。如果继承了父进程的 stdio
,则子进程会继续附着在控制终端。customFds
允许指定特定文件描述符作为子进程的
stdio。该 API 无法移植到所有平台,因此被移除。使用 customFds
可以将新进程的 [stdin,
stdout, stderr]
钩到已有流上;-1
表示创建新流。自己承担使用风险。child_process.exec()
和 child_process.fork()
child_process.exec(command, [options], callback)
-
command
{String} 将要执行的命令,用空格分隔参数 -
options
{Object}-
cwd
{String} 子进程的当前工作目录 -
env
{Object} 环境变量键值对 -
encoding
{String} 编码(缺省为 ‘utf8‘) -
shell
{String} 运行命令的 shell(UNIX 上缺省为 ‘/bin/sh‘,Windows 上缺省为 ‘cmd.exe‘。该 shell 在 UNIX 上应当接受-c
开关,在 Windows 上应当接受/s /c
开关。在 Windows 中,命令行解析应当兼容cmd.exe
。) -
timeout
{Number} 超时(缺省为 0) -
maxBuffer
{Number} 最大缓冲(缺省为 200*1024) -
killSignal
{String} 结束信号(缺省为 ‘SIGTERM‘)
-
-
callback
{Function} 进程结束时回调并带上输出-
error
{Error} -
stdout
{Buffer} -
stderr
{Buffer}
-
- 返回:ChildProcess 对象
在 shell 中执行一个命令并缓冲输出。
var exec = require(‘child_process‘).exec,
child;
child = exec(‘cat *.js bad_file | wc -l‘,
function (error, stdout, stderr) {
console.log(‘stdout: ‘ + stdout);
console.log(‘stderr: ‘ + stderr);
if (error !== null) {
console.log(‘exec error: ‘ + error);
}
});
回调参数为 (error,
stdout, stderr)
。当成功时,error
会是 null
。当遇到错误时,error
会是一个Error
实例,并且 err.code
会是子进程的退出代码,同时 err.signal
会被设置为结束进程的信号名。
第二个可选的参数用于指定一些选项,缺省选项为:
{ encoding: ‘utf8‘,
timeout: 0,
maxBuffer: 200*1024,
killSignal: ‘SIGTERM‘,
cwd: null,
env: null }
如果 timeout
大于
0,则当进程运行超过 timeout
毫秒后会被终止。子进程使用 killSignal
信号结束(缺省为 ‘SIGTERM‘
)。maxBuffer
指定了
stdout 或 stderr 所允许的最大数据量,如果超出这个值则子进程会被终止。
child_process.execFile(file, args, options, callback)
-
file
{String} 要运行的程序的文件名 -
args
{Array} 字符串参数列表 -
options
{Object}-
cwd
{String} 子进程的当前工作目录 -
env
{Object} 环境变量键值对 -
encoding
{String} 编码(缺省为 ‘utf8‘) -
timeout
{Number} 超时(缺省为 0) -
maxBuffer
{Number} 最大缓冲(缺省为 200*1024) -
killSignal
{String} 结束信号(缺省为 ‘SIGTERM‘)
-
-
callback
{Function} 进程结束时回调并带上输出-
error
{Error} -
stdout
{Buffer} -
stderr
{Buffer}
-
- 返回:ChildProcess 对象
child_process.exec()
,但是它不会执行一个子
shell,而是直接执行所指定的文件。因此它稍微比 child_process.exec
精简,参数与之一致。
child_process.fork(modulePath, [args], [options])
-
modulePath
{String} 子进程中运行的模块 -
args
{Array} 字符串参数列表 -
options
{Object}-
cwd
{String} 子进程的当前工作目录 -
env
{Object} 环境变量键值对 -
encoding
{String} 编码(缺省为 ‘utf8‘) -
execPath
{String} 创建子进程的可执行文件
-
- 返回:ChildProcess 对象
spawn()
的特殊情景,用于派生
Node 进程。除了普通 ChildProcess 实例所具有的所有方法,所返回的对象还具有内建的通讯通道。详见 child.send(message,
[sendHandle])
。缺省情况下所派生的
Node 进程的 stdout、stderr 会关联到父进程。要更改该行为,可将 options
对象中的silent
属性设置为 true
。
子进程运行完成时并不会自动退出,您需要明确地调用 process.exit()
。该限制可能会在未来版本里接触。
这些子
Node 是全新的 V8 实例,假设每个新的 Node 需要至少 30 毫秒的启动时间和 10MB 内存,就是说您不能创建成百上千个这样的实例。
options
对象中的 execPath
属性可以用非当前 node
可执行文件来创建子进程。这需要小心使用,并且缺省情况下会使用子进程上的 NODE_CHANNEL_FD
环境变量所指定的文件描述符来通讯。该文件描述符的输入和输出假定为以行分割的
JSON 对象。