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 相关文章推荐
Javascript实现的鼠标经过时播放声音
May 18 Javascript
40款非常棒的jQuery 插件和制作教程(系列一)
Oct 26 Javascript
js 判断上传文件大小及格式代码
Nov 13 Javascript
jquery提交form表单简单示例分享
Mar 03 Javascript
javascript实现复选框超过限制即弹出警告框的方法
Feb 25 Javascript
JS+CSS实现大气清新的滑动菜单效果代码
Oct 22 Javascript
浅谈js的异步执行
Oct 18 Javascript
jQuery插件zTree实现删除树节点的方法示例
Mar 08 Javascript
Node.js 利用cheerio制作简单的网页爬虫示例
Mar 01 Javascript
详解JS预解析原理
Jun 16 Javascript
Vue项目配置跨域访问和代理proxy设置方式
Sep 08 Javascript
js canvas实现俄罗斯方块
Oct 11 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
PHP 网页过期时间的控制代码
2009/06/29 PHP
php比较多维数组中值的大小排序实现代码
2012/09/08 PHP
Laravel中日期时间处理包Carbon的简单使用
2017/09/21 PHP
js图片延迟加载的实现方法及思路
2013/07/22 Javascript
jQuery打印图片pdf、txt示例代码
2014/07/22 Javascript
jQuery实现TAB风格的全国省份城市滑动切换效果代码
2015/08/24 Javascript
js实现带圆角的多级下拉菜单效果
2015/08/28 Javascript
WEB前端开发都应知道的jquery小技巧及jquery三个简写
2015/11/15 Javascript
详解WordPress开发中get_current_screen()函数的使用
2016/01/11 Javascript
JavaScript常用函数工具集:lao-utils
2016/03/01 Javascript
理解javascript对象继承
2016/04/17 Javascript
Javascript中常用类型的格式化方法小结
2016/12/26 Javascript
JS实现iframe自适应高度的方法示例
2017/01/07 Javascript
JS跨域请求外部服务器的资源
2017/02/06 Javascript
javascript+html5+css3自定义弹出窗口效果
2017/10/26 Javascript
ES6 javascript中Class类继承用法实例详解
2017/10/30 Javascript
从vue源码解析Vue.set()和this.$set()
2018/08/30 Javascript
JavaScript适配器模式原理与用法实例详解
2020/03/09 Javascript
《javascript设计模式》学习笔记一:Javascript面向对象程序设计对象成员的定义分析
2020/04/07 Javascript
[02:19]DOTA选手解说齐贺岁
2018/02/11 DOTA
Python实现的多线程端口扫描工具分享
2015/01/21 Python
python如何读取bin文件并下发串口
2019/07/05 Python
Python matplotlib实时画图案例
2020/04/23 Python
django为Form生成的label标签添加class方式
2020/05/20 Python
Python字符串对齐、删除字符串不需要的内容以及格式化打印字符
2021/01/23 Python
2019年c语言经典面试题目
2016/08/17 面试题
业绩倒数第一的检讨书
2014/09/24 职场文书
合伙购房协议样本
2014/10/06 职场文书
服务员岗位职责
2015/02/03 职场文书
南湾猴岛导游词
2015/02/09 职场文书
飞屋环游记观后感
2015/06/08 职场文书
公司老总年会致辞
2015/07/30 职场文书
严以修身专题学习研讨会发言材料
2015/11/09 职场文书
vue中三级导航的菜单权限控制
2021/03/31 Vue.js
Ajax请求超时与网络异常处理图文详解
2021/05/23 Javascript
Android学习之BottomSheetDialog组件的使用
2022/06/21 Java/Android