child_process是node中的一个内置模块,
它提供了衍生(spawn)子进程的功能。
1. fork 示例
index.js
const { fork } = require('child_process');
console.log('1. start fork');
const child = fork('./child.js');
console.log('2. start on message');
child.on('message', data => {
console.log(`9. end message: ${data.y}`);
});
console.log('3. start send');
child.send({ x: 1 });
console.log('4. end send');
child.js
console.log('5. in child - start message');
process.on('message', data => {
console.log(`6. in child - end message: ${data.x}`);
console.log('7. in child - start send');
process.send({ y: 2 });
console.log('8. in child - end send');
});
则日志信息如下,
$ node index.js
1. start fork
2. start on message
3. start send
4. end send
5. in child - start message
6. in child - end message: 1
7. in child - start send
8. in child - end send
9. end message: 2
(挂住)
fork会建立一个永久的channel,以供进程间通信。
因此,以上日志打印完后,会挂住。
可以使用child.disconnect()关闭这个通道。
child.on('message', function () {
console.log('9. end message');
// 关闭channel
child.disconnect();
});
2. spawn 示例
child_process.spawn()
方法使用给定的 command
和 args
中的命令行参数来衍生一个新进程。
如果省略 args
,则默认为一个空数组。
2.1 调用shell命令
const { spawn } = require('child_process');
const child = spawn('ls', ['-lh', '/usr']);
child.stdout.on('data', data => {
console.log(`stdout: ${data}`);
});
child.stderr.on('data', data => {
console.log(`stderr: ${data}`);
});
child.on('close', code => {
console.log(`子进程退出码:${code}`);
});
它会执行命令行操作,
$ ls -lh /usr
2.2 利用 stdin 和 stdout 与子进程通信
index.js
const { spawn } = require('child_process');
console.log('1. start spawn');
const child = spawn('node', ['./child.js']);
console.log('2. start stdout on data');
child.stdout.on('data', data => {
// 子进程 console.log 也会触发,使用随机数区分
console.log(`${Math.random()}: stdout on data - ${data}`);
});
console.log('3. start child on close');
child.on('close', code => {
console.log(`12. child on close: ${code}`);
});
console.log('4. child stdin write');
child.stdin.write('[x]');
console.log('5. child stdin end write');
child.js
console.log('6. in child - start process stdin on data');
process.stdin.on('data', data => {
console.log(`7. child stdin on data: ${data}`);
console.log('8. child process stdout write');
// 与 console.log 效果一样,只是不带末尾换行符
process.stdout.write('[y]');
console.log('9. child process stdout end write');
console.log('10. child process exit');
process.exit(1);
console.log('11. child process end exit'); // 不触发
});
日志信息如下,
$ node index.js
1. start spawn
2. start stdout on data
3. start child on close
4. child stdin write
5. child stdin end write
0.5171850730160528: stdout on data - 6. in child - start process stdin on data
0.4755742447483853: stdout on data - 7. child stdin on data: [x]
0.02516364602172283: stdout on data - 8. child process stdout write
[y]9. child process stdout end write
10. child process exit
12. child on close: 1
注:
(1)子进程的 console.log
也会触发子进程的 stdout
回调,
因此,这里写了一个随机数,以标识不同的触发。
child.stdout.on('data', data => {
// 子进程 console.log 也会触发,使用随机数区分
console.log(`${Math.random()}: stdout on data - ${data}`);
});
(2)process.exit(1);
调用后,子进程立即结束,
因此后续的 console.log
没有触发。
console.log('11. child process end exit'); // 不触发
(3)spawn
后,子进程就启动了,只不过启动是异步的,
落后于主进程的 child.stdin.write('[x]');
。
保持 child.js 不变, 修改 index.js,
const { spawn } = require('child_process');
console.log('1. start spawn');
const child = spawn('node', ['./child.js']);
console.log('2. start stdout on data');
child.stdout.on('data', data => {
console.log(`${Math.random()}: stdout on data - ${data}`);
});
则日志信息如下,
node index.js
1. start spawn
2. start stdout on data
0.9460758839302479: stdout on data - 6. in child - start process stdin on data
(挂住)
(4)子进程中的 process.stdout.write('[y]');
效果与conosle.log
相同,
只是末尾不带换行符,参考日志,
[y]9. child process stdout end write
3. 其他
- child_process.exec(): 衍生一个 shell 并在 shell 上运行命令,当完成时会传入 stdout 和 stderr 到回调函数。
- child_process.execFile(): 类似 child_process.exec(),但直接衍生命令,且无需先衍生 shell。
- child_process.fork(): 衍生一个新的 Node.js 进程,并通过建立 IPC 通讯通道来调用指定的模块,该通道允许父进程与子进程之间相互发送信息。
- child_process.execSync(): child_process.exec() 的同步函数,会阻塞 Node.js 事件循环。
- child_process.execFileSync(): child_process.execFile() 的同步函数,会阻塞 Node.js 事件循环。
参考
node v8.12.0 docs: child_process
node v10.12.0 中文文档:子进程