Node.js进程管理与子进程模块详解

作者:星河时间:2026-10-23 15:44:54浏览:169

本文详细介绍Node.js中process全局对象的功能,包括内存、工作目录及信号处理。重点讲解child_process模块中spawn、fork、exec和execFile四种创建子进程的方法,对比其通信机制与适用场景,帮助开发者优化Node.js单线程模型下的性能瓶颈。

本文通过实例解析Node.js中process进程的核心概念,并演示如何使用child_process子进程模块。内容旨在为读者提供实用的参考指南。

进程可视为正在运行的程序实例。在Node.js环境中,每个应用程序均对应一个进程类的实例对象。

Node.js提供了process全局对象,利用该对象可以获取运行程序的用户信息、环境变量等关键数据。

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++对象占用的外部内存部分。

对象、字符串和闭包存储在堆内存中,变量存储在栈内存,而JavaScript源代码则存放在代码段。


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()可以计算代码段的运行时间。该函数返回一个数组,第一个元素表示秒,第二个元素表示纳秒。


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的使用

二、子进程模块child_process的使用

Node.js采用单线程模型,若某项操作消耗大量资源或时间,会导致整体程序性能下降。

可以通过创建子进程来处理耗时费力的操作,而主线程继续执行其他任务。

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

通过spawn()创建子进程

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。


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);

通过fork()创建子进程

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: '你好父进程'});
});

通过exec()和execFile()创建子进程

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程序设计有所帮助。

作者最新文章