目录

  • 概述
  • 创建子进程的方法
  • 子进程的事件

1、概述

我们都知道 Node.js 是以单线程的模式运行的,但它使用的是事件驱动来处理并发,这样有助于我们在多核 cpu 的系统上创建多个子进程,从而提高性能。

每个子进程总是带有三个流对象:child.stdin, child.stdoutchild.stderr。他们可能会共享父进程的 stdio 流,或者也可以是独立的被导流的流对象。

2、创建子进程的方法

创建子进程有四种方法:spawnexecexecFile, fork

  • child_process.exec(): 衍生一个 shell 并在 shell 上运行命令,当完成时会传入 stdoutstderr 到回调函数。
  • 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 事件循环。

2.1 spawn

child_process.spawn(command[, args][, options])
- command : 要运行的命令
- args : 字符串参数列表。
- options: 选项,其中 cwd 可以指定子进程的当前工作目录。detached 可以设置 true 或者 false,为 true,则将子进程独立于父进程来运行,父进程结束后,子进程可以继续执行。 stdio 用于配置子进程与父进程之间建立的管道

child_process.spawn() 方法使用给定的 commandargs 中的命令行参数来衍生一个新进程。 如果省略 args,则默认为一个空数组。

const { spawn } = require('child_process');
const ls = spawn('ls', ['-lh', '/usr']);ls.stdout.on('data', (data) => {console.log(`stdout: ${data}`);
});ls.stderr.on('data', (data) => {console.log(`stderr: ${data}`);
});ls.on('close', (code) => {console.log(`子进程退出码:${code}`);
});

spawn 生成的子进程实例,有 stdoutstderr 对象,可以监听数据的输入。

再看一个 spawn 方法中参数比较完整的例子:

const process = require('process');
const cp = require('child_process');
let sp1 = cp.spawn('node', ['test1.js', 'one', 'two', 'three'], {cwd: './one'})
let sp2 = cp.spawn('node', ['test2.js'], {stdio: 'pipe'});
sp1.stdout.on('data', (data)=>{console.log('子进程 sp1 标注输出:', data);sp2.stdin.write(data);
});sp1.on('exit', (code, signal)=>{console.log('子进程 sp1 退出,退出代码为', code);process.exit();
});

2.2 exec

child_process.exec(command[, options][, callback])
- command : 运行的命令,参数使用空格分隔。
- options: 选项。cwd: 子进程的当前工作目录。encoding: 默认为 'utf8'maxBuffer: stdout 或 stderr 允许的最大字节数。默认为 200*1024。如果超过限制,则子进程会被终止。
- callback(error, stdout, stderr): 进程终止时调用

exec 创建子进程的时候,会衍生一个 shell 并在 shell 中执行 command,且缓冲任何产生的输出。 传入函数的 command 字符串会被 shell 直接处理。

const { exec } = require('child_process');exec('find . -type f | wc -l', (err, stdout, stderr) => {if (err) {console.error(`exec error: ${err}`);return;}console.log(`Number of files ${stdout}`);
});

2.2.1 spawnexec 方法的相同点:

  • 它们都用于开一个子进程执行指定命令。

  • 它们都可以自定义子进程的运行环境。

  • 它们都返回一个 ChildProcess 对象,所以他们都可以取得子进程的标准输入流,标准输出流和标准错误流 。

2.2.2 spawnexec 方法的不同点:

  • 接受参数的方式: spawn 使用了参数数组,而 exec 则直接接在命令后。

  • 子进程返回给Node的数据量: spawn 没有限制子进程可以返回给Node的数据大小,而 exec 则在 options 配置对象中有 maxBuffer 参数限制,且默认为200K,如果超出,那么子进程将会被杀死,并报错:Error:maxBuffer exceeded,虽然可以手动调大 maxBuffer 参数,但是并不被推荐。由此可窥见一番Node.js设置这两个API时的部分本意, spawn 应用来运行返回大量数据的子进程,如图像处理,文件读取等。而 exec 则应用来运行只返回少量返回值的子进程,如只返回一个状态码。

  • 回调函数: exec 方法相比 spawn 方法,多提供了一个回调函数,可以更便捷得获取子进程输出。这与为返回的 ChildProcess 对象的 stdoutstderr 监听 data 事件来获得输出的区别在于: data 事件的方式,会在子进程一有数据时就触发,并把数据返回给Node。而回调函数,则会先将数据缓存在内存中(数据量小于 maxBuffer 参数),等待子进程运行完毕后,再调用回调函数,并把最终数据交给回调函数。

因为 exec 方法会在将所有的数据缓冲起来,所以有 maxBuffer 大小的限制,因此当数据量小,并且需要使用 shell 语法来创建子进程的时候,使用 exec 方法是个不错的选择。

然而,当数据量比较大的时候,只好选择 spawn 方法来创建子进程,这是因为 spawn 方法中数据是以 stream 的方式来保存的。

2.3 execFile

child_process.execFile(file[, args][, options][, callback])
- file : 要运行的可执行文件的名称或路径。
- args: 字符串参数列表
- options: 选项
- callback(error, stdout, stderr): 当进程终止时调用,并带上输出。

child_process.execFile() 函数类似 child_process.exec(),除了不衍生一个 shell。 而是,指定的可执行的 file 被直接衍生为一个新进程,这使得它比 child_process.exec() 更高效。

const { execFile } = require('child_process');
const child = execFile('node', ['--version'], (error, stdout, stderr) => {if(error){throw error;}console.log(stdout);
});

2.4 fork

child_process.fork(modulePath[, args][,options])
- modulePath : 要在子进程中运行的模块。
- args : 字符串参数列表。
- options: 选项

fork 方法是 spawn 方法的变形的一种形式,也是用来创建进程。二者最大的不同之处在于:fork 方法会创建一个内置的通信信道,允许消息在父进程和子进程之间来回传递。以下有个例子:

parent.js 文件中的代码如下:

const {fork} = require('child_process');const forked = fork('child.js');forked.on('message', (msg) => {console.log('Message from child:',  msg);
});forked.send({hello: 'world'});

child.js 文件中的代码如下:

process.on('message', (msg) => {console.log('Message from parent: ' , msg);
});let counter = 0;setInterval(() => {process.send({counter: counter ++});
}, 1000);

运行 node parent.js ,得到如下结果:

Message from parent:  { hello: 'world' }
Message from child: { counter: 0 }
Message from child: { counter: 1 }
Message from child: { counter: 2 }
Message from child: { counter: 3 }
Message from child: { counter: 4 }
...
...

parent.js 中,使用 fork 方法创建了一个子进程实例 forked,并且指定了子进程的的运行目录为 child.js 。给实例 forked 绑定了 message 事件,当子进程(child.js)中使用 process.send 发送数据的时候,会触发 message 事件。

同时,父进程向子进程中发送数据的时候,通过 forked.send 来进行,参数为一个对象。

3、子进程的事件

ChildProcess 类的实例是 EventEmitter,代表衍生的子进程。

ChildProcess 的实例不被直接创建。 而是,使用 child_process.spawn()child_process.exec()child_process.execFile()child_process.fork() 方法创建 ChildProcess 实例。

3.1 close 事件

当子进程的 stdio 流被关闭时会触发 'close' 事件。 这与 'exit' 事件不同,因为多个进程可能共享同一 stdio 流。

3.2 disconnect 事件

在父进程中调用 subprocess.disconnect() 或在子进程中调用 process.disconnect() 后会触发 'disconnect' 事件。 断开后就不能再发送或接收信息,且 subprocess.connected 属性会被设为 false

3.4 message 事件

当一个子进程使用 process.send() 发送消息时会触发 'message' 事件。

const {fork} = require('child_process');const forked = fork('child.js');forked.on('message', (msg) => {console.log('Message from child:',  msg);
});forked.send({hello: 'world'});

参考资料

Node.js 官方文档:child_process 子进程

Node.js Child Processes: Everything you need to know

Node.js中spawn与exec的异同比较

Node-内置模块:子进程 child_process相关推荐

  1. 关于Vite 客户端代码不支持node内置模块path的处理

    关于Vite 客户端不支持node内置模块path的处理 js客户端代码使用了path模块 解决方案 其他 js客户端代码使用了path模块 import 'path' from 'path' ... ...

  2. 子进程child_process的spawn模块使用

    child_process介绍 使用 在package.json添加运行指令 {"scripts": {"start": "node start.js ...

  3. node内置模块中fs文件系统模块

    fs模块是Node.js官方提供的,用来操作文件的模块.它提高了一系列的方法和属性,用来满足用户对文件的操作需求.fs模块中,所有的方法分为同步和异步两种实现.有 sync 后缀的方法为同步方法,没有 ...

  4. node内置模块——Buffer模块(缓冲区)

    文章目录 Buffer(缓冲区) 创建Buffer 利用字符串创建buffer:Buffer.from 使用Buffer方法创建buffer:Buffer.alloc() Buffer.allocUn ...

  5. 子进程会继承父进程的哪些内容_【学习教程】Node.js创建子进程方法

    来源 | https://github.com/CommanderXL/biu-blog/issues/25 exec 其中exec可用于在指定的shell当中执行命令.不同参数间使用空格隔开,可用于 ...

  6. node child_process模块学习笔记

    NodeJs是一个单进程的语言,不能像Java那样可以创建多线程来并发执行.当然在大部分情况下,NodeJs是不需要并发执行的,因为它是事件驱动性永不阻塞.但单进程也有个问题就是不能充分利用CPU的多 ...

  7. Node.js中的child_process模块详解

    本文主要给大家介绍了关于Node.js中child_process模块的相关内容,在介绍child_process模块之前,先来看一个例子. const http = require('http'); ...

  8. shell脚本spawn_如何使用child_process.spawn将Python / Ruby / PHP Shell脚本与Node.js集成

    shell脚本spawn There are occasions when running a Python/Ruby/PHP shell script from Node.js is necessa ...

  9. Node.js 有难度的面试题,你能答对几个?

    点击上方 前端Q,关注公众号 回复加群,加入前端Q技术交流群 1.Node 模块机制 1.1 请介绍一下 node 里的模块是什么 Node 中,每个文件模块都是一个对象,它的定义如下: functi ...

最新文章

  1. JS实现HTML标签转义及反转义
  2. 1001. [BJOI2006]狼抓兔子【最小割】
  3. mysql字符集相关问题_MySQL 字符集相关问题
  4. linux下运行yolo,Ubuntu下CPU/GPU模式YOLOv3代码运行
  5. 元宇宙“性骚扰”现象频出,Meta推出“个人结界”能保护好女玩家吗?
  6. 使用JDBC-ODBC桥接方式访问Access数据库(实例)
  7. SQL server中的SELECT查询语句执行顺序
  8. C#静态构造函数调用机制
  9. Java入门到月入2W+ IT各种技术教学资料分享
  10. FreeSurfer Tutorial Datasets训练数据配置
  11. ssis中数据类型_SSIS中的数据挖掘查询
  12. php post不完整,如何在PHP中检查不完整的POST请求
  13. RSA加密解密(附源码工程)
  14. java新手笔记7 找最小、最大、排序
  15. BP神经网络用于预测
  16. VMWare IOS MAC分区教程
  17. oracle dbview用户,关于SQLRecoverableException问题的排查和分析
  18. Typora导入CSDN
  19. unity fatal error in gc too many heap sections报错
  20. LINUX-查看历史操作记录

热门文章

  1. 搭建Window10 VNC远程访问ubuntu20.04
  2. OJ 1085 搭数字Ⅱ
  3. Ubuntu WPS系统缺失字体symbol、wingdings、wingdings wingdings webding
  4. Omorn - NJ301-1100 AND NX102-9000 - CIP - UCMM 通讯
  5. CSP-J复赛复习题目(NOIP普及组2000-2011)
  6. git 错误error: failed to push some refs to
  7. Pandas库的学习使用(一)
  8. S32K14x CAN休眠唤醒的实现方案
  9. 【ChatGPT 中文版插件】无需注册体验 ChatGPT 的攻略
  10. 文件无法上传到ftp服务器,无法上传文件到FTP服务器使用C++