child_process - 子進(jìn)程

進(jìn)程

- 定義

  1. 進(jìn)程是程序的執(zhí)行示例
  2. 程序在 CPU 上執(zhí)行的活動(dòng)叫做進(jìn)程
  3. 實(shí)際上并沒(méi)有明確的定義,只有一些規(guī)則

了解 CPU

- 特點(diǎn)

  1. 一個(gè)單核 CPU,在一個(gè)時(shí)刻,只能做一件事情
  2. 那么如何讓用戶同時(shí)看電影、聽(tīng)音樂(lè)、寫(xiě)代碼的呢?
  3. 答案是在不同的進(jìn)程中快速切換(有多快呢?主要看 CPU 的主頻,每秒幾百萬(wàn)次也有可能)

多程序并發(fā)執(zhí)行

  1. 指多個(gè)程序在宏觀上并行,微觀上串行
  2. 每個(gè)進(jìn)程會(huì)出現(xiàn)【執(zhí)行 - 暫停 - 執(zhí)行】的規(guī)律
  3. 多個(gè)進(jìn)程之間會(huì)出現(xiàn)搶資源(如打印機(jī))的現(xiàn)象

阻塞

- 等待進(jìn)程中的進(jìn)程中
  1. 都是非運(yùn)行的狀態(tài)
  2. 一些(A)在等待 CPU 資源
  3. 另一些(B)在等待 I/O 完成(如文件讀?。?/li>
  4. 如果這個(gè)時(shí)候把 CPU 分配給 B 進(jìn)程,B 還是在等待 I/O
  5. 我們把這個(gè) B 叫做阻塞進(jìn)程
  6. 因此,分派程序只會(huì)把CPU 分配給非阻塞進(jìn)程

進(jìn)程的三個(gè)狀態(tài)

線程 Thread

- 分階段

在 linux 2.4 之前,操作系統(tǒng)只有進(jìn)程沒(méi)有線程

  1. 在面向進(jìn)程設(shè)計(jì)的系統(tǒng)中,進(jìn)程是程序的基本執(zhí)行實(shí)體
  2. 在面向線程設(shè)計(jì)的系統(tǒng)中,進(jìn)程本身不是基本運(yùn)行單位,還是線程的容器

- 引入原因

  1. 進(jìn)程是執(zhí)行的基本實(shí)體,也是資源分配的基本實(shí)體
  2. 導(dǎo)致進(jìn)程的創(chuàng)建。切換、銷毀太消耗CPU 時(shí)間了
  3. 于是引入線程,線程作為執(zhí)行的基本實(shí)體
  4. 而進(jìn)程只作為資源分配的基本實(shí)體

Node.js 的進(jìn)程控制

Node.js 的線程控制

概念

  1. CPU 調(diào)度和執(zhí)行的最小單位
  2. 一個(gè)進(jìn)程中至少有一個(gè)線程,可以有多個(gè)線程
  3. 一個(gè)進(jìn)程中的線程共享該進(jìn)程的所有資源
  4. 進(jìn)程的第一個(gè)線程叫做初始化線程
  5. 線程的調(diào)度可以由操作系統(tǒng)負(fù)責(zé),也可以用戶自己負(fù)責(zé)

舉例

  1. 瀏覽器進(jìn)程里面有渲染引擎、V8引擎、儲(chǔ)存模塊、網(wǎng)絡(luò)模塊。用戶界面模塊等
  2. 每個(gè)模塊都可以放在一個(gè)線程里

分析

  • 子進(jìn)程 VS 線程
  • 都能滿足重開(kāi)一個(gè)子任務(wù),優(yōu)先使用線程,除非你需要單獨(dú)的資源分配

Node.js 中的 child_process (用于新建子進(jìn)程)

- 使用目的

  1. 子進(jìn)程的運(yùn)行結(jié)果儲(chǔ)存在系統(tǒng)緩存之中(最大200kb)
  2. 等到子進(jìn)程運(yùn)行結(jié)束以后,主進(jìn)程再用回調(diào)函數(shù)讀取子進(jìn)程的運(yùn)行結(jié)果
簡(jiǎn)單的 exec 栗子:
const child_process = require('child_process')

const { exec } = child_process

const userInput = '-al && pwd' // '-al && rm -fm *'

exec(`ls ${userInput}`, (error, stdout, stderr) => {
  console.log(error)
  console.log(stdout)
  console.log(stderr)
})

有漏洞,可以被注入,可能執(zhí)行意外的代碼(如上 userInput)
所以推薦使用 execFile(因?yàn)閰?shù)通過(guò)另外傳參)
const child_process = require('child_process')

const { execFile } = child_process

const options = { pwd: 'C:\\', env: { NODE_ENV: 'development' } }
// options 常用的選項(xiàng)
// cwd - Current working directory
// env 環(huán)境變量
// shell 用什么 shell
// maxBuffer 最大緩存,默認(rèn) 1024 * 1024 字節(jié)
const userInput = '-al && pwd' // '-al && rm -fm *'
// 這里會(huì)報(bào)錯(cuò)
execFile('ls', ['-al', userInput], options, (error, stdout, stderr) => {
  console.log(error)
  console.log(stdout)
  console.log(stderr)
})

相比上面更推薦使用 spawn
  1. 用法和 execFile 方法類似
  2. 沒(méi)有回調(diào)函數(shù),只能通過(guò)流事件獲取結(jié)果
  3. 沒(méi)有最大 200 kb 的限制(因?yàn)槭橇鳎?/li>
const child_process = require('child_process')

const { spawn } = child_process

const userInput = '.'
const options = { pwd: 'C:\\', env: { NODE_ENV: 'development' } }
const streams = spawn('ls', ['-al', userInput], options)
streams.stdout.on('data', chunk => {
  console.log(chunk.toString())
})

但是我們最常用的還是 fork

  1. 創(chuàng)建一個(gè)子進(jìn)程,執(zhí)行 Node 腳本(正因?yàn)槿绱?,我們大多?shù)時(shí)候都是執(zhí)行 Node 腳本而不是 Bash,所以一般都用 fork)
  2. fork('./child.js) 相當(dāng)于 spawn('node', ['./child.js'])
特點(diǎn)
  1. 會(huì)多出一個(gè) message 事件,用于父子通信
  2. 會(huì)多出一個(gè) send 方法

father.js

const child_process = require('child_process')

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

child.on('message', message => {
  console.log(message)
})

// 或父給子發(fā)信息
// child.send({ hello: 'world' })

child.js

setTimeout(() => {
  process.send('這是 child 傳來(lái)的消息')
}, 3000)

process.on('message', message => {
  console.log('這里接受父?jìng)髯拥南?)
  console.log(message)
})

一些歷史

為什么不用線程,因?yàn)樘铝?,而且效率不夠高?a target="_blank">文檔 中文中寫(xiě)明)

  • child_process.exec
  • v0.1.90 加入 Node.js
  • new Worker
  • v10.5.0 加入 Node.js (去年才加入)
  • v11.7.0 之前需要 --experimental-worker 開(kāi)啟

簡(jiǎn)單介紹 worker_threads

  • api 列表
  1. isMainThread
  2. new Worker(filename)
  3. parentPort
  4. postMessage
  • 事件列表
  1. message
  2. exit
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容