Node.js之child_process

150 阅读2分钟

进程

定义

进程是程序的执行实例,也可以说程序在CPU上执行的活动叫做进程

特点

一个进程可以创建另一个进程(父进程与子进程),可以通过任务管理器查看Chrome的进程

CPU

一个单核CPU在一个时刻只能做一件事情,通过在不同进程之间快速切换实现同时让用户做多件事情

多个程序在宏观上并行,微观上串行,每个进程会出现『执行-暂停-执行』的规律,多个进程之间会出现抢资源的现象

进程的状态

image.png

线程

进程是资源分配的基本实体,线程是执行的基本实体,CPU调度和执行的最小单元

一个进程中的线程可以共享进程的所有资源,进程的第一个线程叫做初始化线程,也是主线程,线程的调度可以由操作系统负责,也可以用户自己负责

Node.js的进程控制

通过 child_process这个模块实现,有几个常用的API,接下来依次介绍

exec

语法 exec(cmd[,options],fn)

代码实例

const { exec } = require('node:child_process')

exec('ls ../', (error, stdout, stderr) => {
  console.log(error)
  console.log(stdout)
  console.log(stderr)
})

exec可以返回两个流

stream.stdout stream.stderr

const child_process = require('child_process')
const { exec } = child_process

const stream = exec('ls -l ../')

stream.stdout.on('data', (chunk) => {
  // 注意不是stream.on
  process.stdout.write(chunk)
})

stream.on('end', () => {})

还可以通过util.promisify封装exec,返回一个promise避免回调地狱

const child_process = require('child_process')
const { exec } = child_process
const util = require('util')

const exec2 = util.promisify(exec) // api都可以用util.promisify封装

exec2('ls -l ../').then((data) => {
  console.log(data)
  console.log(data.stdout)
})

但是 exec有漏洞,cmd可以被注入

const child_process = require('child_process')
const { exec } = child_process
const util = require('util')

const exec2 = util.promisify(exec)
const userInput = '. && pwd' // /Users/wangdanmeng/Desktop/child_process

exec2(`ls ${userInput}`).then((data) => {
  console.log(data.stdout)
})

execFile

语法 child_process.execFile(file[, args][, options][, callback])

可避免注入攻击,也返回两个流

const child_process = require('child_process')
const { execFile } = child_process

const userInput = '. && pwd'

execFile('ls', ['-la', userInput], (error, stdout) => {
  console.log(error)
  console.log(stdout)
})

spawn

没有回调,通过流事件获取结果,相比execFile,更推荐使用这个

const child_process = require('child_process')
const { spawn } = child_process

const stream = spawn('ls')

stream.stdout.on('data', (chunk) => {
  console.log(chunk.toString())
})

fork

创建一个子进程,执行Node脚本

fork('./child.js')相当于spawn('node',['child.js'])

有message和send方法,实现父子进程之间通信,如下面代码所示。控制台会先打印出子进程得到值: { hello: 'world' },三秒后打印出父进程得到值 { foo: 'bar' }

// n.js
const child_process = require('child_process')

const n = child_process.fork('./child.js')

n.on('message', function (m) {
  console.log('父进程得到值: ', m)
})

n.send({ hello: 'world' })


// child.js
process.on('message', function (m) {
  console.log('子进程得到值:', m)
})
setTimeout(() => {
  process.send({ foo: 'bar' })
}, 3000)