深入研究Electron的主進程和渲染進程

主進程“和“渲染進程”是Electron的兩個核心的概念。

如果你之前做的是瀏覽器端JavaScript開發,多進程的概念對你來說可能是一個新的領域。

最初對我來說,這絕對是一個思維方式的轉變,使用多進程可能意味着我們需要在開發過程中做出跟之前不同的設計抉擇。

爲什麼Electron具有這種多進程架構?主進程職責是什麼?渲染進程的職責是什麼?它們之間如何實現通信?

首先,我們這裏所說的“進程”是什麼?

一個操作系統級別的進程,或者如Wikipedia所述,它是“正在執行的計算機程序的實例”。

我們啓動一個Electron應用程序,然後在macOS中檢查“活動監視器”,則可以看到與該程序關聯的進程數。

 

“ Electron”是主進程,一個“ Electron Helper”是GPU進程,另一個“ Electron Helpers”是渲染進程。

 

這些進程中的每一個彼此併發運行。這裏要記住的最重要的一點是,進程的內存和資源是相互隔離的。

舉例來說,假設我有一個模塊,該模塊可以保存我的主進程和渲染進程所需的某些狀態:

 

 

 

 

如果我在渲染進程中增加1,則渲染進程中的計數將爲1,但在主進程中仍爲0。

這兩個進程不共享內存或狀態。實際上,該模塊有兩個實例在運行。

爲什麼要多個進程?

此架構決策源自Chromium。Chromium在單獨的進程中運行每個選項卡(即webContents實例),因此,如果一個選項卡遇到致命錯誤,則不會關閉整個應用程序。從這個意義上說,“ Chromium像操作系統一樣構建,使用多個OS進程將網站彼此隔離,並與瀏覽器本身隔離。” 因此,每個進程“在其自己的地址空間中運行,由操作系統調度,並且可以單獨失敗”。當有人一步小心地寫了一個無限循環,然後關閉正在運行的選項卡,而不是整個瀏覽器。這種體系結構要感謝這種彈性。還有安全原因。Chromium的多進程體系結構文檔非常有趣:https://www.chromium.org/developers/design-documents/multi-process-architecture

主進程

主進程負責創建和管理BrowserWindow實例以及各種應用程序事件。它還可以執行諸如註冊全局快捷方式,創建系統菜單和對話框,響應自動更新事件等操作。應用程序的入口點將指向將在主進程中執行的JavaScript文件。主進程以及所有node.js模塊中都提供了一部分Electron API(請參見下圖)。

 

docs聲明:“基本規則是:如果模塊與GUI或低級系統相關,則它應僅在主進程l中可用。” (請注意,此處的GUI是指本機GUI,而不是Chromium呈現的基於HTML的UI)。這是爲了避免潛在的內存泄漏問題。

渲染進程

渲染過程負責運行應用程序的用戶界面,換句話說,就是作爲webContents實例的網頁。渲染進程中提供了所有DOM API,node.js API和Electron API的子集(請參見下圖)。

我曾經將BrowserWindow與渲染進程混合在一起。在窗口中包含webContents實例之前,實際上不會創建渲染進程。這有點挑剔,但我認爲這很重要,並且知道它會導致更堅實的概念基礎。另外,一個或多個WebContent可以位於一個窗口中。等等,一個或多個?是的,因爲單個窗口可以承載多個Web視圖,並且每個Web視圖都是其自己的webContents實例和渲染進程。因此,例如,如果您的頁面中包含2個Web視圖,則將有3個渲染進程-一個用於託管2個Web視圖的父級,然後一個用於每個Web視圖。話雖如此,除非您要運行遠程網頁,否則無需使用Web視圖,因此不必太在意該細節。

 

 

 

上圖顯示了每種進程中可用的Electron API。您還可以看到Node.js API全局可用,而渲染進程中只有DOM / Browser API。

 

 

如何在進程之間進行通信?

Electron使用進程間通信(IPC)在進程之間進行通信-與Chromium相同。

IPC有點像在網頁和iframe或webWorker之間使用postMessage。

一般來說,發送的消息會帶有頻道名稱和一些其他信息。

IPC可以在渲染進程和主進程之間雙向通信。IPC默認情況下是異步的,但也具有同步API(例如Node.js中的fs)。

Electron還爲提供了遠程模塊,例如,您可以使用主處理模塊,就像Menu渲染器中可用的一樣。

不需要進行手動IPC調用,但實際上是在幕後,您是通過同步IPC調用向主進程發出命令。

使用devtron模塊,我們可以觀察到使用遠程模塊時發生的所有IPC調用。同步IPC調用可能會有性能缺陷。在許多情況下,可能還不錯。

 

 

 

 

remote用於打開對話框時,會發生多個同步IPC調用。

使用ipc或遠程API的代碼非常簡單。一個基本的示例可以在這裏找到: ccnokes / electron-tutorials-小樣本電子應用程序的集合

有沒有一個方法是主進程和渲染進程都支持的?

是的,可以通過remote模塊訪問主進程API,例如:

 

 

 

IPC是否在底層使用了某些網絡協議(例如tcp,http或更瘋狂的東西)?

不。Chromium的IPC文檔指出,它使用“命名管道”作爲IPC的基礎工具。命名管道比網絡協議可以提供更快,更安全的通信。

“命名管道”類似於“無名管道”,當您執行類似操作時使用ls | grep foo。命名管道很有趣,

您可以在此處查看示例:https : //github.com/ccnokes/node-fifo-example

那麼我在哪裏進行CPU密集型工作?

我曾經認爲 主進程 是“繁重工作”的理想之地,因爲它不會阻塞UI。

實際上,這是錯誤的—如果我們在主進程中執行CPU密集型工作,它將鎖定所有渲染進程。

因此,CPU密集型任務應在單獨的進程中運行-而不是任何包含UI的現有渲染進程或主進程。

其實,最簡單的方法是使用Electron-remote。Electron-remote非常棒,並且具有渲染進程任務池,該任務池將跨多個進程拆分和平衡作業。

這是一個簡單的例子:

 

 

 

 

 

 

在這段代碼中electron remore 最多創建4個BrowserWindow實例,所有這些實例都需要您的模塊並運行它,然後協調進程之間的來回通信。

 

 

附:IPC通信的幾種方式

 

數據傳輸:一個進程需要將它的數據發送給另一個進程,發送的數據量在一個字節到幾M字節之間
共享數據:多個進程想要操作共享數據,一個進程對共享數據的修改,別的進程應該立刻看到。
通知事件:一個進程需要向另一個或一組進程發送消息,通知它(它們)發生了某種事件(如進程終止時要通知父進程)。
資源共享:多個進程之間共享同樣的資源。爲了作到這一點,需要內核提供鎖和同步機制。
進程控制:有些進程希望完全控制另一個進程的執行(如Debug進程),此時控制進程希望能夠攔截另一個進程的所有陷入和異常,並能夠及時知道它的狀態改變。

linux常用的進程間的通訊方式

(1)、管道(pipe):管道可用於具有親緣關係的進程間的通信,是一種半雙工的方式,數據只能單向流動,允許一個進程和另一個與它有共同祖先的進程之間進行通信。

(2)、命名管道(named pipe):命名管道克服了管道沒有名字的限制,同時除了具有管道的功能外(也是半雙工),它還允許無親緣關係進程間的通信。命名管道在文件系統中有對應的文件名。命名管道通過命令mkfifo或系統調用mkfifo來創建。

(3)、信號(signal):信號是比較複雜的通信方式,用於通知接收進程有某種事件發生了,除了進程間通信外,進程還可以發送信號給進程本身;linux除了支持Unix早期信號語義函數sigal外,還支持語義符合Posix.1標準的信號函數sigaction(實際上,該函數是基於BSD的,BSD爲了實現可靠信號機制,又能夠統一對外接口,用sigaction函數重新實現了signal函數)。

(4)、消息隊列:消息隊列是消息的鏈接表,包括Posix消息隊列system V消息隊列。有足夠權限的進程可以向隊列中添加消息,被賦予讀權限的進程則可以讀走隊列中的消息。消息隊列克服了信號承載信息量少,管道只能承載無格式字節流以及緩衝區大小受限等缺

(5)、共享內存:使得多個進程可以訪問同一塊內存空間,是最快的可用IPC形式。是針對其他通信機制運行效率較低而設計的。往往與其它通信機制,如信號量結合使用,來達到進程間的同步及互斥。

(6)、內存映射:內存映射允許任何多個進程間通信,每一個使用該機制的進程通過把一個共享的文件映射到自己的進程地址空間來實現它。

(7)、信號量(semaphore):主要作爲進程間以及同一進程不同線程之間的同步手段。

(8)、套接字(Socket):更爲一般的進程間通信機制,可用於不同機器之間的進程間通信。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支持套接字。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章