node.js中process进程的概念和child_process子进程模块的使用方法示例


Posted in Javascript onFebruary 11, 2020

本文实例讲述了node.js中process进程的概念和child_process子进程模块的使用方法。分享给大家供大家参考,具体如下:

进程,你可以把它理解成一个正在运行的程序。node.js中每个应用程序都是进程类的实例对象。

node.js中有一个 process 全局对象,通过它我们可以获取,运行该程序的用户,环境变量等信息。

一、process 对象

console.log('可执行文件绝对路径', process.execPath);
console.log('版本号', process.version);
console.log('依赖库的版本号', process.versions);
console.log('运行平台', process.platform);
console.log('标准输入流', process.stdin);
console.log('标准输出流', process.stdout);
console.log('标准错误流', process.stderr);
console.log('命令行参数数组', process.argv);
console.log('系统环境变量', process.env);
console.log('进程ID', process.pid);
console.log('标题', process.title);
console.log('处理器架构', process.arch);

通过 memoryUsage() 查看内存使用量:

console.log(process.memoryUsage());
  • rss 表示进程占用的内存,包括堆,栈,代码段。
  • heapTotal 表示堆占用的内存。
  • heapUsed 表示堆使用的部分。
  • external 表示外部使用的部分,C++对象占用的。

对象,字符串,闭包存放于堆内存,变量存放于栈内存,js源代码存放于代码段。

console.log(process.memoryUsage());
let buf = Buffer.alloc(1024 * 1024 * 1024);
console.log(process.memoryUsage());

当我们通过Buffer创建一个足够大的变量时,这时只能借助于外部内存,使用C++去完成。node.js能够使用的内存上限是1.7G。

使用 chdir() 修改程序当前的工作目录,通过 cwd() 获取当前工作目录。

console.log(process.cwd());
//修改程序当前的工作目录
process.chdir('../');
console.log(process.cwd());

通过 exit() 来结束进程

process.exit(0);

调用 exit() 结束进程时,会触发 'exit' 事件。

process.on('exit', function () {
  console.log('程序退出了');
});
process.exit(0);

通过 kill() 给指定进程发送信号

SIGINT 程序终止信号,当用户按下ctrl+c时发出,将通知进程终止。

SIGTERM 程序结束信号,通知程序正常退出,kill()方法默认就是这个信号。

process.kill(process.pid, 'SIGINT');

通过 uptime() 返回程序运行的时间

console.log(process.uptime());

通过 hrtime() 计算代码段运行时间,hrtime() 返回一个数组,第一个表示秒,第二个表示纳秒

let start = process.hrtime();
let sum = 0;
for (i = 0; i < 1000000000; i++) {
  sum += i;
}
let end = process.hrtime(start);
console.log('耗时 : ', end[0], '秒');

当程序抛出一个没有被捕获的异常时,触发 'uncaughtException' 事件。

process.on('uncaughtException', function (err) {
  console.log('捕获了一个未被处理的异常');
  console.log(err);
});
//调用一个未定义的函数
test();

进程接收到一个信号时,会触发信号事件,我们可以监听到该事件。

//让标准输入流处于流动模式,让程序无法退出
process.stdin.resume();
process.on('SIGINT', function () {
  console.log('程序退出');
  process.exit(0);
});
process.on('SIGTERM', function () {
  console.log('程序结束');
});

二、子进程模块child_process的使用

我们都知道node.js是单线程的,如果某一个操作需要消耗大量资源和时间,会导致程序整体性能下降。

我们可以创建子进程,让子进程去跑那些费时费力的操作,而主线程该干嘛干嘛。

子进程间可以共享内存,通过互相通信来完成数据的交换。

1、通过 spawn() 创建子进程

const {spawn} = require('child_process');
//参数一表示,要执行的命令
//参数二表示,运行该命令的参数
//参数三表示,创建子进程的配置
let cp1 = spawn('node', ['1.js'], {
  //cwd表示当前子进程的工作目录
  cwd: process.cwd(),
  //子进程的环境变量
  env: process.env,
  //子进程的标准输入,标准输出,错误,的配置
  //pipe表示,父进程与子进程间建立管道,父进程可以访问子进程对应的输入,输出,和错误
  //ipc表示,父进程与子进程间建立一个专门用来传递消息的IPC通道,子进程调用send()方法向子进程发送消息,并触发'message'事件
  //ignore表示,忽略子进程的标准输入,标准输出,错误。
  //inherit表示,子进程共享父进程的标准输入,标准输出,错误。
  //stream表示,父进程与子进程共享一个流,比如文件流或socket。
  //正整数表示,父进程打开的文件描述符,与子进程共享,比如文件的fd。类似stream流对象共享。
  //null或undefined表示,父进程与子进程间创建管道
  stdio: ['pipe', process.stdout, 'pipe'],
  //子进程是否独立于父进程运行
  detached: false
});

1.js的代码:

console.log('hello');

运行代码后,我们可以看到子进程的 'hello',出现在了父进程的标准输出上。因为 stdio 的配置,我们让子进程与父进程共享标准输出。

spawn() 会返回一个子进程对象,我们可以监听该对象的一些事件。

const {spawn} = require('child_process');
let cp1 = spawn('node', ['1.js'], {
  cwd: process.cwd(),
  env: process.env,
  stdio: ['pipe', process.stdout, 'pipe'],
  detached: false
});
//子进程所有输入/输出终止时,会触发子进程的 'close' 事件
cp1.on('close', function (code, signal) {
  //当父进程关闭子进程时,signal表示父进程发送给子进程的信号名称
  console.log('子进程关闭了', code, signal);
});
//子进程退出时,会触发 'exit' 事件
//注意,子进程退出,子进程的输入/输出有可能并未关闭。因为输入/输出有可能多个进程共享。
cp1.on('exit', function (code, signal) {
  console.log('子进程退出', code, signal);
});
//子进程出错时,触发
cp1.on('error', function (err) {
  console.log(err);
});

注意,stdio 设置成 pipe ,是把子进程的stdin,stdout,stderr导向了 spawn() 返回的子进程对象的stdin,stdout,stderr。

然后父进程就可以通过子进程对象访问stdin,stdout,stderr。

const {spawn} = require('child_process');
let cp1 = spawn('node', ['1.js'], {
  cwd: process.cwd(),
  env: process.env,
  stdio: ['pipe', 'pipe', 'pipe'],
  detached: false
});
//监听子进程标准输入,输出,错误的数据。
cp1.stdin.on('data', function (data) {
  console.log(data.toString());
});
cp1.stdout.on('data', function (data) {
  console.log(data.toString());
});
cp1.stderr.on('data', function (data) {
  console.log(data.toString());
});

1.js的代码:

//往子进程标准输出中写入数据
console.log('我是标准输出');
//往子进程错误中写入数据
console.error('我是一个错误');
//往子进程标准输入中写入数据
process.stdin.write('我是标准输入');

当我们设置 stdio 为 ipc 时,会创建一个父进程与子进程传递消息的IPC通道。

const {spawn} = require('child_process');
let cp1 = spawn('node', ['1.js'], {
  cwd: process.cwd(),
  env: process.env,
  //注意这里,子进程只能有一个IPC通道
  stdio: ['pipe', 'ipc', 'pipe'],
  detached: false
});
//注意这里要用子进程对象进行监听
//监听有没有消息
cp1.on('message', function (data) {
  console.log('子进程发送的 : ', data.toString());
});
cp1.send('你好,子进程');

1.js的代码:

process.on('message', function (data) {
  console.log('父进程发送的 : ', data.toString());
});
//向父进程发送消息
process.send('你好,父进程');

默认情况下,只有子进程全部退出了,父进程才能退出。我们希望父进程退出了,子进程仍然独立运行。可以通过设置 detached 为 true。

默认情况下,父进程会等待所有子程退出后,才退出。通过使用 unref() 让父进程无需等待子进程就可直接退出。

const {spawn} = require('child_process');
const fs = require('fs');
let fd = fs.openSync('./1.txt', 'w', 0o666);
let cp1 = spawn('node', ['1.js'], {
  cwd: process.cwd(),
  //注意这里,把不需要的设置为ignore,不然主进程仍然会阻塞等待子进程
  stdio: ['ignore', fd, 'ignore'],
  detached: true
});
cp1.on('error', function (err) {
  console.log(err);
});
//解绑子进程,让父进程不用等待子进程
cp1.unref();

1.js的代码:

let i = 0;
let timer = setInterval(function () {
  if (i > 20) {
    clearInterval(timer);
  }
  process.stdout.write('写入数据' + i + '\r\n');
  i++;
}, 1000);

2、通过 fork() 创建子进程

fork() 是 spawn() 的特殊情况,用于创建新的进程,默认建立一个IPC通信通道,允许父进程与子进程进行消息传递。

fork() 返回一个子进程对象,子进程输入/输出操作执行完毕后,父进程不退出,子进程不会自动退出,需调用 exit() 显式退出。

const {fork} = require('child_process');
//参数一表示,运行的模块
//参数二表示,参数列表
//参数三表示,创建子进程的配置
let cp1 = fork('2.js', ['1', '2', '3'], {
  //子进程的工作目录
  cwd: process.cwd(),
  //子进程的环境变量
  env: process.env,
  //运行模块的可执行文件
  execPath: process.execPath,
  //传递给可执行文件的参数列表
  execArgv: process.execArgv,
  //为false表示父进程与子进程共享标准(输入/输出),为true时不共享。
  silent: false
});
cp1.on('error', function (err) {
  console.log(err);
});

2.js的代码:

for (let i = 0; i < process.argv.length; i++) {
  process.stdout.write(process.argv[i] + '\r\n');
}

父进程与子进程间,通过 send() 和 'message'事件来传递消息。

const {fork} = require('child_process');
let cp1 = fork('2.js', [], {
  cwd: process.cwd(),
  env: process.env,
  silent: false
});
//接收消息
cp1.on('message', function (data) {
  console.log('父进程收到 : ', JSON.stringify(data));
  process.exit(0);
});
//发送消息
cp1.send({name: '你好子进程'});

2.js的代码:

process.on('message', function (data) {
  console.log('子进程收到 : ', JSON.stringify(data));
  process.send({name: '你好父进程'});
});

3、通过exec() 创建子进程

exec() 可以开启一个子进程运行命令,并缓存子进程的输出结果。

const {exec} = require('child_process');
//参数一表示,要运行的命令
//参数二表示,配置选项
//参数三表示,进程终止时的回调
exec('dir', {
  //子进程的工作目录
  cwd: process.cwd(),
  //子进程的环境变量
  env: process.env,
  //输出的编码
  encoding: 'utf8',
  //超时时间
  timeout: 60 * 1000,
  //缓存stdout,stderr最大的字节数
  maxBuffer: 1024 * 1024,
  //关闭子进程的信号
  killSignal: 'SIGTERM'
}, function (err, stdout, stderr) {
  console.log(stdout.toString());
});

4、通过 execFile() 创建子进程

使用 execFile() 开启一个运行可执行文件的子进程。

const {execFile} = require('child_process');
//参数一表示,可执行文件的名称或路径
//参数二表示,参数列表
//参数三表示,配置选项
//参数四表示,进程终止时的回调
let cp1 = execFile('node', ['3.js', '1', '2', '3'], {
  //子进程的工作目录
  cwd: process.cwd(),
  //子进程的环境变量
  env: process.env,
  //输出的编码
  encoding: 'utf8',
  //超时时间
  timeout: 60 * 1000,
  //缓存stdout,stderr最大的字节数
  maxBuffer: 1024 * 1024,
  //关闭子进程的信号
  killSignal: 'SIGTERM'
}, function (err, stdout, stderr) {
  if (err) {
    console.log(err);
    process.exit();
  }
  console.log('子进程的输出 : ', stdout.toString());
});
cp1.on('error', function (err) {
  console.log(err);
});

3.js的代码:

process.argv.forEach(function (value) {
  process.stdout.write(value + '\r\n');
});

fork(),exec(),execFile() 都是基于 spawn() 的封装。

希望本文所述对大家node.js程序设计有所帮助。

Javascript 相关文章推荐
W3C Group的JavaScript1.8 新特性介绍
May 19 Javascript
基于MVC3方式实现下拉列表联动(JQuery)
Sep 02 Javascript
以JSON形式将JS中Array对象数组传至后台的方法
Jan 06 Javascript
jQuery如何防止Ajax重复提交
Oct 14 Javascript
ES6中箭头函数的定义与调用方式详解
Jun 02 Javascript
Vue.js常用指令的使用小结
Jun 23 Javascript
不到200行 JavaScript 代码实现富文本编辑器的方法
Jan 03 Javascript
javascript高仿热血传奇游戏实现代码
Feb 22 Javascript
vue数组对象排序的实现代码
Jun 20 Javascript
JavaScript动态创建二维数组的方法示例
Feb 01 Javascript
Vue 实现对quill-editor组件中的工具栏添加title
Aug 03 Javascript
JavaScript实现两个数组的交集
Mar 25 Javascript
小程序如何定位所在城市及发起周边搜索
Feb 11 #Javascript
JS+DIV实现拖动效果
Feb 11 #Javascript
vue项目中使用particles实现粒子背景效果及遇到的坑(按钮没有点击响应)
Feb 11 #Javascript
原生js拖拽实现图形伸缩效果
Feb 10 #Javascript
js实现单元格拖拽效果
Feb 10 #Javascript
详解Vue template 如何支持多个根结点
Feb 10 #Javascript
JavaScript canvas动画实现时钟效果
Feb 10 #Javascript
You might like
生成sessionid和随机密码的例子
2006/10/09 PHP
收藏的PHP常用函数 推荐收藏保存
2010/02/21 PHP
遍历指定目录下的所有目录和文件的php代码
2011/11/27 PHP
PHP上传图片进行等比缩放可增加水印功能
2014/01/13 PHP
PHP数字金额转换成中文大写显示
2019/01/05 PHP
详解php命令注入攻击
2019/04/06 PHP
JS array 数组详解
2009/03/22 Javascript
让低版本浏览器支持input的placeholder属性(js方法)
2013/04/03 Javascript
网页禁用右键菜单和鼠标拖动选择方法小结
2015/02/25 Javascript
jQuery多条件筛选如何实现
2015/11/04 Javascript
jQuery实现选中行变色效果(实例讲解)
2017/07/06 jQuery
ajax+node+request爬取网络图片的实例(宅男福利)
2017/08/28 Javascript
vue首次赋值不触发watch的解决方法
2018/09/11 Javascript
VUE实现图片验证码功能
2020/11/18 Javascript
vue cli3.0打包上线静态资源找不到路径的解决操作
2020/08/03 Javascript
vue项目实现减少app.js和vender.js的体积操作
2020/11/12 Javascript
使用Python对Access读写操作
2017/03/30 Python
django框架自定义用户表操作示例
2018/08/07 Python
python Pexpect 实现输密码 scp 拷贝的方法
2019/01/03 Python
python实现两个经纬度点之间的距离和方位角的方法
2019/07/05 Python
Python 计算任意两向量之间的夹角方法
2019/07/05 Python
python实现批量nii文件转换为png图像
2019/07/18 Python
Python面向对象程序设计之私有变量,私有方法原理与用法分析
2020/03/23 Python
Python使用多进程运行含有任意个参数的函数
2020/05/02 Python
Python如何实现的二分查找算法
2020/05/27 Python
canvas实现图片马赛克的示例代码
2018/03/26 HTML / CSS
Kate Spade澳大利亚官方网站:美国设计师手袋品牌
2019/09/10 全球购物
2019年Java 最常见的 面试题
2016/10/19 面试题
PHP使用Redis队列执行定时任务实例讲解
2021/03/24 PHP
生物专业个人自荐信范文
2013/11/29 职场文书
群众路线党课主持词
2014/04/01 职场文书
计算机专业求职信
2014/06/02 职场文书
2014党委书记四风问题对照检查材料思想汇报
2014/09/22 职场文书
党员检讨书范文
2014/12/27 职场文书
Python字典的基础操作
2021/11/01 Python
关于的python五子棋的算法
2022/05/02 Python