原文鏈接:http://www.chromium.org/developers/design-documents/multi-process-architecture
問題
創造一個從來不會崩潰或者掛起的渲染引擎是幾乎不可能。同樣,創造一個絕對安全的渲染引擎也非常的困難。
從某些方面說,現在的瀏覽器就像早期的單用戶共享多任務操作系統一樣。一個應用程序出錯可能導致整個系統的崩潰,現代瀏覽器的一個tab也一樣。例如,一個瀏覽器或者插件的bug可能導致整個瀏覽器的崩潰。
架構總攬
對於每個瀏覽器的tab,我們分別使用不同的進程,這樣可以避免渲染引擎出錯導致整個瀏覽器的崩潰。我們限制每個渲染引擎進程相互之間以及對系統其他部分的訪問。這樣給你瀏覽器帶來的好處就像內存地址空間隔離和訪問控制給操作系統帶來的好處一樣。
我們在主進程顯示UI,並像管理“瀏覽器進程”或者“瀏覽器”一樣管理tab和插件。同樣,每個tab對應的進程叫做“渲染進程”或者“渲染器”。每個渲染器使用WebKit作爲佈局引擎,解析並渲染HTML。
管理渲染進程
每個渲染進程都有一個全局的RenderProcess對象負責和當前瀏覽器進程之間的通信,並保存全局狀態。 對應的,對於每一個渲染進程瀏覽器維護一個RenderProcessHost,它用來管理瀏覽器狀態以及和渲染進程通信。瀏覽器和渲染器之間的通信通過Chromium的IPC系統。
組件和接口
在渲染進程中:
- RenderProcess和瀏覽器進程中一個對應的RenderProcessHost通信。每一個渲染進程有一個RenderProcess。
- RenderView對象和瀏覽器進程中對應的RenderViewHost進行通信(通過RenderProcess)。這個對象代表每個web page和彈出窗口的內容。
在瀏覽器進程中:
- Browser對象代表最頂層的瀏覽器窗口。
- RenderProcessHost代表瀏覽器端的一個和渲染進程之間的IPC連接。在瀏覽器中,一個RenderProcessHost對應一個渲染器進程。
- RenderViewHost封裝和遠端RenderView之間的通信,RenderWidgetHost處理RenderWidget的輸入和打印。
共享渲染進程
通常情況下,每個窗口或tab啓動一個新的進程。瀏覽器會啓動一個新進程並指示它創建一個RenderView對象。
有時候必須在多個tab和window之間共享渲染進程。比如,web應用打開一個新窗口並和他同步通信,在JavaScript使用window.open。這種情況下,新打開的窗口或tab需要重用之前的渲染進程。 ,在進程達到最大值的時候或者窗口已經打開時,我們也需要重用之前的渲染進程。重用的側率策略在“Process Models”中有詳細描述。
監測崩潰或渲染器的異常行爲
每個通過IPC通信的瀏覽器進程都監測渲染進程句柄。如果句柄收到信號則表示渲染進程已經崩潰。目前爲止,我們顯示一個“sad tab”的圖標,通知用戶渲染進程崩潰。頁面可以通過點擊刷新按鈕重新載入,或者打開一個新頁面。
渲染器和沙盒
Webkit運行在一個獨立的進程中,我們可能會限制他訪問系統資源。例如,我們保證渲染器只可以訪問其父瀏覽器進程當前顯示的域名。同樣,我們可以用宿主操作系統提供的權限特性限制它訪問磁盤文件系統。
除了限制渲染進程訪問文件系統和網絡,我們也會限制它訪問用戶顯示對象或其他對象。這樣防止受到控制的渲染器打開新窗口或記錄鍵盤輸入。
內存的回收
渲染引擎運行在單獨的進程中,這樣可以很方便的將當前不顯示的頁面(進程)設置爲低的優先級。通常情況下,在Windows上最小化窗口會自動將其內存放進“可用內存”池中。 在內存不足的情況下,Windows會將首先將這些內存放入交換空間,這樣有助於用戶可見的程序有更好的響應速度。我們也對隱藏的tab應用這種策略,對於隱藏的tab會通知操作系統在必要時優先將這些內存交換入磁盤。因爲我們發現減少內存佔用也會降低tab切換的性能,所以我們逐漸釋放內存。當內存充足的時候這些都不是問題。
這樣處理有助於在內存較小的系統上獲得更優的內存佔用。不常使用的tab所佔用的內存可以被整個交換到磁盤,當前tab的內存可以全部放在內存中。相反,單進程的瀏覽器將導致它的內存隨機分佈,無法這樣明確的區分出正在使用的和未使用的內存,這樣更浪費內存和性能。
插件
和fireFox風格類似的NPAPI插件運行在他們自己的進程空間,和渲染器分離。在“Plugin Architecture”中有詳細描述。