child_process是Node.js的一个十分重要的模块,通过它可以实现创建多进程,以利用多核计算资源。
Node.js 0.8的child_process
模块提供了四个创建子进程的函数,分别是spawn
,exec
,execFile
和fork
。其中spawn
是最原始的创建子进程的函数,其他三个都是对spawn
不同程度的封装。spawn
只能运行指定的程序,参数需要在列表中给出,相当于execvp
系统函数,而exec
可以直接运行复杂的命令。
例如要运行ls -lh /usr
,使用spawn
需要写成spawn('ls', ['-lh', '/usr'])
,而exec
只需exec('ls -lh /usr')
。exec
的实现原理是启动了一个系统shell来解析参数,因此可以是非常复杂的命令,包括管道和重定向。此外,exec
还可以直接接受一个回调函数作为参数,回调函数有三个参数,分别是err
, stdout
, stderr
,非常方便直接使用,例如:
child_process.exec('ls -lh /usr', function(err, stdout, stderr) {
console.log(stdout);
});
如果使用spawn
,则必须写成:
child = child_process.spawn('ls', ['-lh', '/usr']);
child.stdout.setEncoding('utf8');
child.stdout.on('data', function(data) {
console.log(data);
});
execFile
与spawn
的参数相似,也需要分别指定执行的命令和参数,但可以接受一个回调函数,与exec
的回调函数相同。它与exec
的区别在于不启动独立的shell
,因此相比更加轻量级。
fork
函数用于直接运行Node.js模块,例如fork('./child.js')
,相当于spawn('node', ['./child.js'])
。与默认的spawn
不同的是,fork
会在父进程与子进程直接建立一个IPC管道,用于父子进程之间的通信。例如:
var n = child_process.fork('./child.js');
n.on('message', function(m) {
console.log('PARENT got message:', m);
});
n.send({ hello: 'world' });
child.js
的内容:
process.on('message', function(m) {
console.log('CHILD got message:', m);
});
process.send({ foo: 'bar' });
其中父进程调用fork
函数获取一个返回值,作为子进程的句柄,通过send
函数发送信息,on('message')
监听返回的信息,子进程通过内置的process
对象相同的方法与父进程通信。
fork
函数有一个问题,就是它只能运行JavaScript代码,如果你喜欢用CoffeeScript(或者其他任何编译到js的语言),是无法通过fork
调用的。一个简单的方法是把代码编译到JavaScript再运行,但是很不方便,有没有什么办法呢?
答案是可以的,还是得回到spawn
函数。spawn
函数除了接受command
, args
外,还接受一个options
参数。通过把options
参数的stdio
设为['ipc']
,即可在父子进程之间建立IPC管道。例如子进程使用CoffeeScript:
child_process = require('child_process')
options =
stdio: ['ipc']
child = child_process.spawn 'coffee', ['./child.coffee'], options
其中只要把spawn
的第一个参数设置为运行对应脚本的解释器,即可运行,例如使用Continuation.js,只需child = child_process.spawn('continuation', ['./child.coffee'], options)
。