除了想入門瀏覽器內(nèi)核開發(fā)的讀者之外,這篇文章對于CEF(Chromium Embedded Framework)開發(fā)者也有一定的參考意義。CEF是一個(gè)將Chromium瀏覽器引擎嵌入到其他應(yīng)用程序的框架。了解Chromium的高級架構(gòu)及其如何將其劃分為多個(gè)進(jìn)程類型,對于CEF開發(fā)者來說非常有幫助。通過了解Chromium的架構(gòu),開發(fā)者可以更好地理解CEF如何運(yùn)作,并在開發(fā)過程中作出更明智的決策。本文討論了Chromium的多進(jìn)程架構(gòu)、進(jìn)程間通信、渲染器沙盒化和內(nèi)存管理等概念,這些概念在使用CEF時(shí)同樣適用。
原文地址:https://www.chromium.org/developers/design-documents/chromeviews/
多進(jìn)程架構(gòu)
本文檔描述了Chromium的高級架構(gòu)以及如何在多個(gè)進(jìn)程類型之間進(jìn)行劃分。
問題
構(gòu)建一個(gè)永不崩潰或掛起的渲染引擎幾乎是不可能的。同樣,構(gòu)建一個(gè)完全安全的渲染引擎也幾乎是不可能的。
在某種程度上,2006年左右的網(wǎng)絡(luò)瀏覽器的狀態(tài)類似于過去單用戶、協(xié)同多任務(wù)操作系統(tǒng)的狀態(tài)。在這樣的操作系統(tǒng)中,一個(gè)行為不端的應(yīng)用程序可能導(dǎo)致整個(gè)系統(tǒng)崩潰,而一個(gè)行為不端的網(wǎng)頁在網(wǎng)絡(luò)瀏覽器中也可能如此。一個(gè)渲染引擎或插件錯(cuò)誤就足以使整個(gè)瀏覽器和所有正在運(yùn)行的標(biāo)簽頁崩潰。
現(xiàn)代操作系統(tǒng)更加健壯,因?yàn)樗鼈儗?yīng)用程序放入相互隔離的單獨(dú)進(jìn)程中。一個(gè)應(yīng)用程序的崩潰通常不會影響其他應(yīng)用程序或操作系統(tǒng)的完整性,并且每個(gè)用戶訪問其他用戶數(shù)據(jù)的權(quán)限受到限制。Chromium的架構(gòu)旨在實(shí)現(xiàn)這種更強(qiáng)大的設(shè)計(jì)。
譯者注語:“多進(jìn)程模型在不可避免地 一些問題和復(fù)雜性的同時(shí),也帶來了了更多的優(yōu)勢,而且這些優(yōu)勢非常的重要。該模型至少有三點(diǎn)好處:其一是避免因單個(gè)頁面的不響應(yīng)或者崩潰而影響整個(gè)瀏覽器的穩(wěn)定性,特別是對用戶界面的影響;其二是,當(dāng)?shù)谌讲寮罎r(shí)不會影響頁面或者瀏覽器的穩(wěn)定性,這時(shí)因?yàn)榈谌讲寮脖皇褂脝为?dú)的進(jìn)程來運(yùn)行;其三是,它方便了安全模型的實(shí)施,也就是說沙箱模型是基于多進(jìn)程架構(gòu)的?!禬ebkit技術(shù)內(nèi)幕》”
架構(gòu)概述
Chromium使用多個(gè)進(jìn)程來保護(hù)整個(gè)應(yīng)用程序免受渲染引擎或其他組件的錯(cuò)誤和故障影響。它還限制了每個(gè)渲染引擎進(jìn)程對其他進(jìn)程和系統(tǒng)其余部分的訪問。在某些方面,這為網(wǎng)絡(luò)瀏覽帶來了操作系統(tǒng)在內(nèi)存保護(hù)和訪問控制方面的優(yōu)勢。
我們將運(yùn)行UI并管理渲染器和其他進(jìn)程的主進(jìn)程稱為“瀏覽器進(jìn)程”或“瀏覽器”。同樣,處理Web內(nèi)容的進(jìn)程稱為“渲染器進(jìn)程”或“渲染器”。渲染器使用Blink開源布局引擎來解釋和布局HTML。

譯者注語:Chromium允許用戶配置Renderer進(jìn)程被創(chuàng)建的方式,默認(rèn)情況下,Chromium為每個(gè)標(biāo)簽頁都創(chuàng)建一個(gè)獨(dú)立的渲染進(jìn)程,而不管它們是否是不同域不同實(shí)例。
管理渲染器進(jìn)程
每個(gè)渲染器進(jìn)程都有一個(gè)全局的RenderProcess對象,該對象負(fù)責(zé)與父瀏覽器進(jìn)程通信并維護(hù)全局狀態(tài)。瀏覽器為每個(gè)渲染器進(jìn)程維護(hù)一個(gè)相應(yīng)的RenderProcessHost,用于管理渲染器的瀏覽器狀態(tài)和通信。瀏覽器和渲染器使用Mojo或Chromium的遺留IPC系統(tǒng)進(jìn)行通信。
譯者注語:“Renderer進(jìn)程在網(wǎng)頁的加載過程中需要獲取資源,但是由于安全性(實(shí)際上,當(dāng)沙箱模型打開的時(shí)候,Renderer進(jìn)程是沒有權(quán)限去獲取資源的)和效率上(資源共享等問題)的考慮,Renderer進(jìn)程的資源獲取實(shí)際上是通過進(jìn)程間通信將任務(wù)交給Browser進(jìn)程來完成,Browser進(jìn)程有權(quán)限從網(wǎng)絡(luò)或者本地獲取資源。——《Webkit技術(shù)內(nèi)幕》”
管理幀和文檔
每個(gè)渲染器進(jìn)程都有一個(gè)或多個(gè)RenderFrame對象,這些對象對應(yīng)于包含內(nèi)容的文檔幀。瀏覽器進(jìn)程中相應(yīng)的RenderFrameHost負(fù)責(zé)管理與該文檔相關(guān)的狀態(tài)。每個(gè)RenderFrame被賦予一個(gè)路由ID,用于區(qū)分同一渲染器中的多個(gè)文檔或幀。這些ID在渲染器內(nèi)部是唯一的,但在瀏覽器內(nèi)部不是唯一的,因此識別幀需要一個(gè)RenderProcessHost和一個(gè)路由ID。從瀏覽器到渲染器中特定文檔的通信是通過這些RenderFrameHost對象完成的,這些對象知道如何通過Mojo或遺留IPC發(fā)送消息。
組件和接口
在渲染器進(jìn)程中:
- RenderProcess處理與瀏覽器中相應(yīng)的RenderProcessHost之間的Mojo設(shè)置和遺留IPC。每個(gè)渲染器進(jìn)程中恰好有一個(gè)RenderProcess對象。
- RenderFrame對象與其在瀏覽器進(jìn)程中的相應(yīng)RenderFrameHost(通過Mojo)以及Blink層進(jìn)行通信。此對象表示標(biāo)簽頁或子框架中的一個(gè)Web文檔的內(nèi)容。
在瀏覽器進(jìn)程中:
- Browser對象表示一個(gè)頂級瀏覽器窗口。
- RenderProcessHost對象表示單個(gè)瀏覽器?渲染器IPC連接的瀏覽器端。每個(gè)渲染器進(jìn)程在瀏覽器進(jìn)程中都有一個(gè)RenderProcessHost。
- RenderFrameHost對象封裝了與RenderFrame的通信,RenderWidgetHost處理瀏覽器中RenderWidget的輸入和繪制。
有關(guān)這種嵌入如何工作的更詳細(xì)信息,請參閱Chromium如何顯示網(wǎng)頁設(shè)計(jì)文檔。
共享渲染器進(jìn)程
通常,每個(gè)新窗口或標(biāo)簽頁都會在一個(gè)新進(jìn)程中打開。瀏覽器將生成一個(gè)新進(jìn)程,并指示它創(chuàng)建一個(gè)單獨(dú)的RenderFrame,該RenderFrame可能在頁面中創(chuàng)建更多的iframe(可能位于不同的進(jìn)程中)。
有時(shí),有必要或者需要在標(biāo)簽頁或窗口之間共享渲染器進(jìn)程。例如,Web應(yīng)用程序可以使用window.open創(chuàng)建另一個(gè)窗口,如果新文檔屬于相同的源,則必須共享相同的進(jìn)程。當(dāng)總進(jìn)程數(shù)量過大時(shí),Chromium還有將新標(biāo)簽頁分配給現(xiàn)有進(jìn)程的策略。這些注意事項(xiàng)和策略在進(jìn)程模型中有詳細(xì)描述。
檢測崩潰或行為異常的渲染器
每個(gè)與瀏覽器進(jìn)程的Mojo或IPC連接都會監(jiān)視進(jìn)程句柄。如果這些句柄被觸發(fā),則渲染器進(jìn)程已經(jīng)崩潰,受影響的標(biāo)簽頁和幀將被通知崩潰。Chromium顯示一個(gè)“悲傷的標(biāo)簽頁”或“悲傷的幀”圖像,通知用戶渲染器已崩潰。用戶可以通過按重新加載按鈕或開始新的導(dǎo)航來重新加載頁面。發(fā)生這種情況時(shí),Chromium會注意到?jīng)]有渲染器進(jìn)程并創(chuàng)建一個(gè)新的渲染器進(jìn)程。
對渲染器進(jìn)行沙盒化
由于渲染器在單獨(dú)的進(jìn)程中運(yùn)行,我們有機(jī)會通過沙盒化來限制其對系統(tǒng)資源的訪問。例如,我們可以確保渲染器只能通過Chromium的網(wǎng)絡(luò)服務(wù)訪問網(wǎng)絡(luò)。同樣,我們可以使用主機(jī)操作系統(tǒng)的內(nèi)置權(quán)限來限制其對文件系統(tǒng)的訪問,或者限制其對用戶顯示和輸入的訪問。這些限制顯著降低了受損渲染器進(jìn)程所能完成的事情。
回收內(nèi)存
由于渲染器在單獨(dú)的進(jìn)程中運(yùn)行,將隱藏的標(biāo)簽頁視為較低優(yōu)先級變得非常簡單。通常情況下,Windows上的最小化進(jìn)程會自動(dòng)將其內(nèi)存放入“可用內(nèi)存”池中。在低內(nèi)存情況下,Windows會在將高優(yōu)先級內(nèi)存交換出去之前,將這些內(nèi)存交換到磁盤上,以保持用戶可見程序的響應(yīng)性。我們可以將這個(gè)原則應(yīng)用到隱藏的標(biāo)簽頁。當(dāng)一個(gè)渲染進(jìn)程沒有頂級標(biāo)簽頁時(shí),我們可以釋放該進(jìn)程的“工作集”大小,作為一個(gè)提示,讓系統(tǒng)在需要時(shí)優(yōu)先將該內(nèi)存交換到磁盤上。我們發(fā)現(xiàn)減小工作集大小也會降低用戶在兩個(gè)標(biāo)簽頁之間切換時(shí)的性能,所以我們會逐步釋放這些內(nèi)存。這意味著,如果用戶切換回最近使用過的標(biāo)簽頁,該標(biāo)簽頁的內(nèi)存比較舊的標(biāo)簽頁更有可能已經(jīng)加載到內(nèi)存中。對于內(nèi)存足夠運(yùn)行所有程序的用戶,他們不會注意到這個(gè)過程:Windows只有在需要時(shí)才會實(shí)際回收這些數(shù)據(jù),所以在內(nèi)存充足的情況下不會有性能損失。
這有助于我們在低內(nèi)存情況下獲得更優(yōu)的內(nèi)存占用。不常用的后臺標(biāo)簽頁的內(nèi)存可以完全交換出去,而前臺標(biāo)簽頁的數(shù)據(jù)可以完全加載到內(nèi)存中。相比之下,單進(jìn)程瀏覽器中所有標(biāo)簽頁的數(shù)據(jù)都會隨機(jī)分布在其內(nèi)存中,無法如此干凈地區(qū)分使用和未使用的數(shù)據(jù),既浪費(fèi)內(nèi)存,也影響性能。
其他進(jìn)程類型
Chromium還將許多其他組件分割成單獨(dú)的進(jìn)程,有時(shí)以平臺特定的方式。例如,它現(xiàn)在有單獨(dú)的GPU進(jìn)程、網(wǎng)絡(luò)服務(wù)和存儲服務(wù)。沙盒化的實(shí)用程序進(jìn)程也可用于較小或高風(fēng)險(xiǎn)的任務(wù),作為滿足安全性的兩項(xiàng)規(guī)則之一的方式。