這篇文章給大家探究一下Node.js 和 Electron 的進程通信原理,介紹一下electron 如何做進程通信、nodejs 的 child_process 和 cluster 如何做進程通信,了解進程通信的本質。
為什麼前端要了解進程通訊:
前端領域已經不是單純寫在瀏覽器裡跑的頁面就可以了,還要會electron、 nodejs 等,而這兩個技術都需要掌握進程通訊。
nodejs 是js 的一個執行時,和瀏覽器不同,它擴充了許多封裝作業系統能力的api,其中就包含進程、執行緒相關api,而學習進程api 就要學習進程之間的通信機制。
electron 是基於 chromium 和 nodejs 的桌面端開發方案,它的架構是一個主進程,多個渲染進程,這兩種進程之間也需要通信,要學習 electron 的進程通信機制。 【推薦學習:《nodejs 教學》】
這篇文章我們就來深入了解一下進程通訊。
本文會說明以下知識點:
- 程式是什麼
- 本機處理通訊的四種方式 ##ipc、lpc、rpc 都是什麼
- electron 如何做進程通訊
- nodejs 的child_process 和cluster 如何做進程通訊
- 進程通訊的本質
#進程
我們寫完的程式碼要在作業系統之上跑,作業系統為了更好的利用硬體資源,支援了多個程式的並發和硬體資源的分配,分配的單位就是進程,這個進程就是程式的執行過程。例如記錄程式執行到哪一步了,就申請了哪些硬體資源、佔用了什麼連接埠等。 進程包含要執行的程式碼、程式碼操作的數據,以及進程控制區塊PCB(Processing Control Block),因為程式就是程式碼在資料集上的執行過程,而執行過程的狀態和申請的資源需要記錄在一個資料結構(PCB)裡。所以進程由程式碼、資料、PCB 組成。進程通訊
不同進程之間因為可用的記憶體不同,所以要透過一個中間媒體通訊。信號量
如果是簡單的標記,透過一個數字來表示,放在PCB 的一個屬性裡,這叫做信號量,例如鎖的實現就可以透過信號量。
管道
但是信號量不能傳遞具體的資料啊,傳遞具體資料還得用別的方式。例如我們可以透過讀寫文件的方式來通信,這就是管道,如果是在記憶體中的文件,叫做匿名管道,沒有文件名,如果是真實的硬碟的文件,是有文件名的,叫做命名管道。
訊息佇列
管道實作簡單,但是同步的通訊比較受限制,那如果想做成非同步通訊呢?加個佇列做緩衝(buffer)不就行了,這就是訊息佇列。
共享記憶體#
管道、訊息佇列都是兩個行程之間的,如果多個行程之間呢?
我們可以透過申請一段多進程都可以操作的內存,叫做共享內存
,用這種方式來通信。各行程都可以向該記憶體讀寫數據,效率比較高。
共享記憶體雖然效率高、也能用於多個進程的通信,但也不全是好處,因為多個進程都可以讀寫,那麼就很容易亂,要自己控制順序,比如通過進程的信號量(標記變數)來控制。
共享記憶體適用於多個進程之間的通信,不需要通過中間介質,所以效率更高,但是使用起來也更複雜。
上面說的這些幾乎就是本地進程通訊的全部方式了,為什麼要加個本地呢?
ipc、rpc、lpc
進程通訊就是ipc(Inter-Process Communication),兩個行程可能是一台電腦的,也可能網路上的不同電腦的進程,所以進程通訊方式分為兩種:
本地過程呼叫LPC(local procedure call)、遠端過程呼叫RPC(remote procedure call)。
本地過程呼叫就是我們上面說的信號量、管道、訊息佇列、共享記憶體的通訊方式,但是如果是網路上的,那就要透過網路協定來通訊了,這個其實我們用的比較多,例如http、websocket。
所以,當有人提到 ipc 時就是在說進程通信,可以分為本地的和遠端的兩種來討論。
遠端的都是基於網路協定封裝的,而本地的都是基於信號量、管道、訊息佇列、共享記憶體封裝出來的,例如我們接下來要探討的 electron 和 nodejs。
electron 進程通訊
electron 會先啟動主進程,然後透過 BrowserWindow 建立渲染進程,載入 html 頁面實作渲染。這兩個進程之間的通訊是透過 electron 提供的 ipc 的 api。
ipcMain、ipcRenderer
主程式裡面透過ipcMain 的on 方法監聽事件
import { ipcMain } from 'electron'; ipcMain.on('异步事件', (event, arg) => { event.sender.send('异步事件返回', 'yyy'); })
渲染進程裡面經過 ipcRenderer 的on 方法監聽事件,透過send 發送訊息
import { ipcRenderer } from 'electron'; ipcRender.on('异步事件返回', function (event, arg) { const message = `异步消息: ${arg}` }) ipcRenderer.send('异步事件', 'xxx')
api 使用比較簡單,這是經過c 層的封裝,然後暴露給js 的事件形式的api。
我們可以想它是基於哪一種機制實現的呢?
很明顯有一定的非同步性,而且是父子程序之間的通信,所以是訊息佇列的方式實現的。
remote
除了事件形式的 api 外,electron 還提供了遠端方法呼叫 rmi (remote method invoke)形式的 api。
其實就是對訊息的進一步封裝,也就是根據傳遞的訊息,呼叫不同的方法,形式上就像呼叫本進程的方法一樣,但其實是發訊息到另一個進程來做的,和ipcMain、ipcRenderer 的形式本質上一樣。
例如在渲染流程裡面,透過 remote 來直接呼叫主程序才有的 BrowserWindow 的 api。
const { BrowserWindow } = require('electron').remote; let win = new BrowserWindow({ width: 800, height: 600 }); win.loadURL('https://github.com');
小結一下,electron 的父子程序通訊方式是基於訊息佇列封裝的,封裝形式有兩種,一種是事件的方式,透過ipcMain、ipcRenderer 的api 使用,另一種則是進一步封裝成了不同方法的呼叫(rmi),底層也是基於訊息,執行遠端方法但是看起來像執行本地方法一樣。
nodejs
nodejs 提供了建立流程的 api,有兩個模組: child_process 和 cluster。很明顯,一個是用於父子進程的創建和通信,一個是用於多個進程。
child_process
child_process 提供了spawn、exec、execFile、fork 的api,分別用於不同的程序的建立:
spawn、exec
如果想透過shell 執行指令,那就用spawn 或exec。因為一般執行指令是需要傳回值的,這兩個 api 在傳回值的方式上有所不同。
spawn 回傳的是 stream,透過 data 事件來取,exec 進一步分裝成了 buffer,使用起來簡單一些,但是可能會超過 maxBuffer。
const { spawn } = require('child_process'); var app = spawn('node','main.js' {env:{}}); app.stderr.on('data',function(data) { console.log('Error:',data); }); app.stdout.on('data',function(data) { console.log(data); });
其實 exec 是基於 spwan 封裝出來的,簡單場景可以用,有的時候要設定下 maxBuffer。
const { exec } = require('child_process'); exec('find . -type f', { maxBuffer: 1024*1024 }(err, stdout, stderr) => { if (err) { console.error(`exec error: ${err}`); return; } console.log(stdout); });
execFile
除了執行指令外,如果要執行執行檔就用execFile 的api:
const { execFile } = require('child_process'); const child = execFile('node', ['--version'], (error, stdout, stderr) => { if (error) { throw error; } console.log(stdout); });
#fork
還有如果是想執行js ,那就用fork:
const { fork } = require('child_process'); const xxxProcess = fork('./xxx.js'); xxxProcess.send('111111'); xxxProcess.on('message', sum => { res.end('22222'); });
小結
簡單小結一下child_process 的4 個api:
如果想要執行shell 指令,用spawn 和exec,spawn 回傳一個stream,而exec 進一步封裝成了buffer。除了 exec 有的時候需要設定下 maxBuffer,其他沒差別。
如果想執行執行文件,用 execFile。
如果想執行 js 文件,用 fork。
child_process 的进程通信
说完了 api 我们来说下 child_process 创建的子进程怎么和父进程通信,也就是怎么做 ipc。
pipe
首先,支持了 pipe,很明显是通过管道的机制封装出来的,能同步的传输流的数据。
const { spawn } = require('child_process'); const find = spawn('cat', ['./aaa.js']); const wc = spawn('wc', ['-l']); find.stdout.pipe(wc.stdin);
比如上面通过管道把一个进程的输出流传输到了另一个进程的输入流,和下面的 shell 命令效果一样:
cat ./aaa.js | wc -l
message
spawn 支持 stdio 参数,可以设置和父进程的 stdin、stdout、stderr 的关系,比如指定 pipe 或者 null。还有第四个参数,可以设置 ipc,这时候就是通过事件的方式传递消息了,很明显,是基于消息队列实现的。
const { spawn } = require('child_process'); const child = spawn('node', ['./child.js'], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] }); child.on('message', (m) => { console.log(m); }); child.send('xxxx');
而 fork 的 api 创建的子进程自带了 ipc 的传递消息机制,可以直接用。
const { fork } = require('child_process'); const xxxProcess = fork('./xxx.js'); xxxProcess.send('111111'); xxxProcess.on('message', sum => { res.end('22222'); });
cluster
cluster 不再是父子进程了,而是更多进程,也提供了 fork 的 api。
比如 http server 会根据 cpu 数启动多个进程来处理请求。
import cluster from 'cluster'; import http from 'http'; import { cpus } from 'os'; import process from 'process'; const numCPUs = cpus().length; if (cluster.isPrimary) { for (let i = 0; i < numCPUs; i++) { cluster.fork(); } } else { const server = http.createServer((req, res) => { res.writeHead(200); res.end('hello world\n'); }) server.listen(8000); process.on('message', (msg) => { if (msg === 'shutdown') { server.close(); } }); }
它同样支持了事件形式的 api,用于多个进程之间的消息传递,因为多个进程其实也只是多个父子进程的通信,子进程之间不能直接通信,所以还是基于消息队列实现的。
共享内存
子进程之间通信还得通过父进程中转一次,要多次读写消息队列,效率太低了,就不能直接共享内存么?
现在 nodejs 还是不支持的,可以通过第三方的包 shm-typed-array 来实现,感兴趣可以看一下。
https://www.npmjs.com/package/shm-typed-array
总结
进程包括代码、数据和 PCB,是程序的一次执行的过程,PCB 记录着各种执行过程中的信息,比如分配的资源、执行到的地址、用于通信的数据结构等。
进程之间需要通信,可以通过信号量、管道、消息队列、共享内存的方式。
信号量就是一个简单的数字的标记,不能传递具体数据。
管道是基于文件的思想,一个进程写另一个进程读,是同步的,适用于两个进程。
消息队列有一定的 buffer,可以异步处理消息,适用于两个进程。
共享内存是多个进程直接操作同一段内存,适用于多个进程,但是需要控制访问顺序。
这四种是本地进程的通信方式,而网络进程则基于网络协议的方式也可以做进程通信。
进程通信叫做 ipc,本地的叫做 lpc,远程的叫 rpc。
其中,如果把消息再封装一层成具体的方法调用,叫做 rmi,效果就像在本进程执行执行另一个进程的方法一样。
electron 和 nodejs 都是基于上面的操作系统机制的封装:
elctron 支持 ipcMain 和 ipcRenderer 的消息传递的方式,还支持了 remote 的 rmi 的方式。
-
nodejs 有 child_process 和 cluster 两个模块和进程有关,child_process 是父子进程之间,cluster 是多个进程:
child_process 提供了用于执行 shell 命令的 spawn、exec,用于执行可执行文件的 execFile,用于执行 js 的 fork。提供了 pipe 和 message 两种 ipc 方式。
cluster 也提供了 fork,提供了 message 的方式的通信。
当然,不管封装形式是什么,都离不开操作系统提供的信号量、管道、消息队列、共享内存这四种机制。
ipc 是开发中频繁遇到的需求,希望这篇文章能够帮大家梳理清楚从操作系统层到不同语言和运行时的封装层次的脉络。
原文地址:https://juejin.cn/post/6988484297485189127
作者:zxg_神说要有光
更多编程相关知识,请访问:编程视频!!
以上是深入了解Node.js和Electron是如何做進程通訊的的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Atom編輯器mac版下載
最受歡迎的的開源編輯器

Dreamweaver CS6
視覺化網頁開發工具

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能