Node-内置模块:子进程 child_process
目录
- 概述
- 创建子进程的方法
- 子进程的事件
1、概述
我们都知道 Node.js 是以单线程的模式运行的,但它使用的是事件驱动来处理并发,这样有助于我们在多核 cpu 的系统上创建多个子进程,从而提高性能。
每个子进程总是带有三个流对象:child.stdin
, child.stdout
和 child.stderr
。他们可能会共享父进程的 stdio
流,或者也可以是独立的被导流的流对象。
2、创建子进程的方法
创建子进程有四种方法:spawn
,exec
,execFile
, fork
。
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 事件循环。
2.1 spawn
child_process.spawn(command[, args][, options])
- command : 要运行的命令
- args : 字符串参数列表。
- options: 选项,其中cwd
可以指定子进程的当前工作目录。detached
可以设置 true 或者 false,为 true,则将子进程独立于父进程来运行,父进程结束后,子进程可以继续执行。stdio
用于配置子进程与父进程之间建立的管道
child_process.spawn()
方法使用给定的 command
和 args
中的命令行参数来衍生一个新进程。 如果省略 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
生成的子进程实例,有 stdout
和 stderr
对象,可以监听数据的输入。
再看一个 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 spawn
和 exec
方法的相同点:
它们都用于开一个子进程执行指定命令。
它们都可以自定义子进程的运行环境。
它们都返回一个
ChildProcess
对象,所以他们都可以取得子进程的标准输入流,标准输出流和标准错误流 。
2.2.2 spawn
和 exec
方法的不同点:
接受参数的方式:
spawn
使用了参数数组,而exec
则直接接在命令后。子进程返回给Node的数据量:
spawn
没有限制子进程可以返回给Node的数据大小,而exec
则在options
配置对象中有maxBuffer
参数限制,且默认为200K,如果超出,那么子进程将会被杀死,并报错:Error:maxBuffer exceeded
,虽然可以手动调大maxBuffer
参数,但是并不被推荐。由此可窥见一番Node.js设置这两个API时的部分本意,spawn
应用来运行返回大量数据的子进程,如图像处理,文件读取等。而exec
则应用来运行只返回少量返回值的子进程,如只返回一个状态码。回调函数:
exec
方法相比spawn
方法,多提供了一个回调函数,可以更便捷得获取子进程输出。这与为返回的ChildProcess
对象的stdout
或stderr
监听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相关推荐
- 关于Vite 客户端代码不支持node内置模块path的处理
关于Vite 客户端不支持node内置模块path的处理 js客户端代码使用了path模块 解决方案 其他 js客户端代码使用了path模块 import 'path' from 'path' ... ...
- 子进程child_process的spawn模块使用
child_process介绍 使用 在package.json添加运行指令 {"scripts": {"start": "node start.js ...
- node内置模块中fs文件系统模块
fs模块是Node.js官方提供的,用来操作文件的模块.它提高了一系列的方法和属性,用来满足用户对文件的操作需求.fs模块中,所有的方法分为同步和异步两种实现.有 sync 后缀的方法为同步方法,没有 ...
- node内置模块——Buffer模块(缓冲区)
文章目录 Buffer(缓冲区) 创建Buffer 利用字符串创建buffer:Buffer.from 使用Buffer方法创建buffer:Buffer.alloc() Buffer.allocUn ...
- 子进程会继承父进程的哪些内容_【学习教程】Node.js创建子进程方法
来源 | https://github.com/CommanderXL/biu-blog/issues/25 exec 其中exec可用于在指定的shell当中执行命令.不同参数间使用空格隔开,可用于 ...
- node child_process模块学习笔记
NodeJs是一个单进程的语言,不能像Java那样可以创建多线程来并发执行.当然在大部分情况下,NodeJs是不需要并发执行的,因为它是事件驱动性永不阻塞.但单进程也有个问题就是不能充分利用CPU的多 ...
- Node.js中的child_process模块详解
本文主要给大家介绍了关于Node.js中child_process模块的相关内容,在介绍child_process模块之前,先来看一个例子. const http = require('http'); ...
- 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 ...
- Node.js 有难度的面试题,你能答对几个?
点击上方 前端Q,关注公众号 回复加群,加入前端Q技术交流群 1.Node 模块机制 1.1 请介绍一下 node 里的模块是什么 Node 中,每个文件模块都是一个对象,它的定义如下: functi ...
最新文章
- JS实现HTML标签转义及反转义
- 1001. [BJOI2006]狼抓兔子【最小割】
- mysql字符集相关问题_MySQL 字符集相关问题
- linux下运行yolo,Ubuntu下CPU/GPU模式YOLOv3代码运行
- 元宇宙“性骚扰”现象频出,Meta推出“个人结界”能保护好女玩家吗?
- 使用JDBC-ODBC桥接方式访问Access数据库(实例)
- SQL server中的SELECT查询语句执行顺序
- C#静态构造函数调用机制
- Java入门到月入2W+ IT各种技术教学资料分享
- FreeSurfer Tutorial Datasets训练数据配置
- ssis中数据类型_SSIS中的数据挖掘查询
- php post不完整,如何在PHP中检查不完整的POST请求
- RSA加密解密(附源码工程)
- java新手笔记7 找最小、最大、排序
- BP神经网络用于预测
- VMWare IOS MAC分区教程
- oracle dbview用户,关于SQLRecoverableException问题的排查和分析
- Typora导入CSDN
- unity fatal error in gc too many heap sections报错
- LINUX-查看历史操作记录
热门文章
- 搭建Window10 VNC远程访问ubuntu20.04
- OJ 1085 搭数字Ⅱ
- Ubuntu WPS系统缺失字体symbol、wingdings、wingdings wingdings webding
- Omorn - NJ301-1100 AND NX102-9000 - CIP - UCMM 通讯
- CSP-J复赛复习题目(NOIP普及组2000-2011)
- git 错误error: failed to push some refs to
- Pandas库的学习使用(一)
- S32K14x CAN休眠唤醒的实现方案
- 【ChatGPT 中文版插件】无需注册体验 ChatGPT 的攻略
- 文件无法上传到ftp服务器,无法上传文件到FTP服务器使用C++