Node.js通过child_process开启子进程执行指定程序。主要包括4个异步进程函数(spawn,exec,execFile,fork)和3个同步进程函数(spawnSync,execFileSync,execSync)。

    异步进程函数spawn是最基本的创建子进程的函数,其他三个异步函数都是对spawn不同程度的封装。spawn只能运行指定的程序,参数需要在列表中给出,而exec可以直接运行复杂的命令。

    默认情况下,stdinstdoutstderr 的管道在父 Node.js 进程和衍生的子进程之间建立。这些管道具有有限的(与平台相关)容量。 如果子进程在没有捕获输出的情况下写入超出该限制的 stdout,则子进程将阻塞等待管道缓冲区接受更多的数据。 这与 shell 中的管道行为相同。 如果不需要输出,则使用 { stdio: 'ignore' }

    1. [child_process.exec()](https://link.zhihu.com/?target=http%3A//nodejs.cn/s/pkpJMy):衍生一个 shell 并在该 shell 中运行命令,当完成时则将stdoutstderr 传给回调函数。
    2. [child_process.execFile()](https://link.zhihu.com/?target=http%3A//nodejs.cn/s/N6uK8q):类似于 [child_process.exec()](https://link.zhihu.com/?target=http%3A//nodejs.cn/s/pkpJMy),除了它默认会直接衍生命令且不首先衍生 shell。
    3. [child_process.fork()](https://link.zhihu.com/?target=http%3A//nodejs.cn/s/VDCJMa):衍生一个新的 Node.js 进程,并通过建立 IPC 通信通道来调用指定的模块,该通道允许在父进程与子进程之间发送消。
    4. [child_process.execSync()](https://link.zhihu.com/?target=http%3A//nodejs.cn/s/i6KxMV)[child_process.exec()](https://link.zhihu.com/?target=http%3A//nodejs.cn/s/pkpJMy) 的同步版本,会阻塞 Node.js 事件循环。
    5. [child_process.execFileSync()](https://link.zhihu.com/?target=http%3A//nodejs.cn/s/ed75fU)[child_process.execFile()](https://link.zhihu.com/?target=http%3A//nodejs.cn/s/N6uK8q) 的同步版本,会阻塞 Node.js 事件循环。
    1. const { spawn } = require('child_process');
    2. const ls = spawn('ls', ['-lh', '/usr']);
    3. ls.stdout.on('data', (data) => {
    4. console.log(`stdout: ${data}`);
    5. });
    6. ls.stderr.on('data', (data) => {
    7. console.log(`stderr: ${data}`);
    8. });
    9. ls.on('close', (code) => {
    10. console.log(`子进程退出码:${code}`);
    11. });
    1. const { exec } = require('child_process');
    2. exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => {
    3. if (error) {
    4. console.error(`执行出错: ${error}`);
    5. return;
    6. }
    7. console.log(`stdout: ${stdout}`);
    8. console.log(`stderr: ${stderr}`);
    9. });

    spawn与exec的相同点
    1、都用于开一个子进程执行指定命令。
    2、都可以自定义子进程的运行环境。
    3、都返回一个ChildProcess对象,所以他们都可以取得子进程的标准输入流、标准输出流和标准错误流。

    spawn与exec的不同点
    1、接受参数的方式:spawn使用了参数数组,而exec则直接接在命令后。
    比如要运行 du -sh /disk1 命令, 使用spawn函数需要写成spawn('du', ['-sh ', '/disk1']),而使用exec函数时,可以直接写成exec('du -sh /disk1')。exec是会先进行Shell语法解析,因此用exec函数可以更方便的使用复杂的Shell命令,包括管道、重定向等。
    2、子进程返回给Node的数据量:spawn没有限制子进程可以返回给Node的数据大小,而exec则在options配置对象中有maxBuffer参数限制,且默认为200K,如果超出,那么子进程将会被杀死,并报错:Error:maxBuffer exceeded,虽然可以手动调大maxBuffer参数,但是并不被推荐。由此可窥见一番Node.js设置这两个API时的部分本意,spawn应用来运行返回大量数据的子进程,如图像处理,文件读取等。而exec则应用来运行只返回少量返回值的子进程,如只返回一个状态码。
    3、exec方法相比spawn方法,多提供了一个回调函数,可以更便捷得获取子进程输出。这与从返回的ChildProcess对象的stdout或stderr监听data事件来获得输出的区别是: data事件的方式,会在子进程一有数据时就触发,并把数据返回给Node。而回调函数,则会先将数据缓存在内存中(数据量小于maxBuffer参数),等待子进程运行完毕后,再调用回调函数,并把最终数据交给回调函数。

    Node.js 的 fork 创建进程是通过 libuv 的 uv_spawn. 在 Unix 平台下 uv_spawn 最终调用了系统的 fork。POSIX 的 fork 需要 waitpid 等方法手动回收, 如果未注意回收可能导致僵尸进程出现。Node.js 通过内建 IPC 来自动处理回收。