【Chromium中文文檔】Chromium多進程架構

多進程架構

轉載請註明出處:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//Start_Here_Background_Reading/Multi-process_Architecture.htmljavascript

有github帳號的話,不妨隨手star一個 https://github.com/ahangchen/Chromium_doc_zhhtml

這個文檔描述了Chromium的高層架構java

問題

構建一個從不會掛起或崩潰的渲染引擎幾乎是不可能的。構建一個徹底安全的渲染引擎也是幾乎不可能的。git

在某種程度上,web瀏覽器當前狀態就像一個與過去的多任務操做系統合做的單獨的用戶。正如在一個這樣的操做系統中的錯誤程序會讓整個系統掛掉,因此一個錯誤的web頁面也可讓一個現代瀏覽器掛掉。僅僅須要一個瀏覽器或插件的bug,就餓能讓整個瀏覽器和全部正在運行的標籤頁中止運行。github

現代操做系統更加魯棒,由於他們把應用程序分紅了彼此隔離的獨立線程。一個程序中的crash一般不會影響其餘程序或整個操做系統,每一個用戶對用戶數據的訪問也是有限制的。web

架構概覽

咱們爲瀏覽器的標籤頁使用獨立的進程,以此保護整個應用程序免受渲染引擎中的bug和故障的傷害。咱們也會限制每一個渲染引擎進程的相互訪問,以及他們與系統其餘部分的訪問。某些程度上,這爲web瀏覽提供了內存保護,爲操做系統提供了訪問控制。chrome

咱們把運行UI的進程叫作主進程(main),把插件進程稱爲「瀏覽器進程」或「瀏覽器(Browser)」。類似的,標籤頁相關的進程被稱做「渲染線程」或「渲染器(renderer)」。渲染器使用WebKit開源引擎來實現中斷與html的佈局。windows

arch

管理渲染進程

每一個渲染進程有一個全局的RenderProcess對象,管理它與父瀏覽器進程之間的通訊,維護全局的狀態。瀏覽器爲每一個渲染進程維護一個對應的RenderViewHost,用來管理瀏覽器狀態,並與渲染器交流。瀏覽器與渲染器使用Chromium’s IPC system進行交流。瀏覽器

管理view

每一個渲染進程有一個以上的RenderView對象,由RenderProcess管理(它與標籤頁的內容相關)。對應的RenderProcessHost維護一個與渲染器中每一個view相關的RenderViewHost。每一個view被賦予一個view ID,以區分同一個渲染器中的不一樣view。這些ID在每一個渲染器內是惟一的,但在瀏覽器中不是,因此區分一個view須要一個RenderProcessHost和一個view ID。安全

瀏覽器與一個包含內容的特定標籤頁之間的交流是經過這些RenderViewHost對象來完成的,它們知道如何經過他們的RenderProcessHost向RenderProcess和RenderView送消息。

組件與接口

在渲染進程中:

  • RenderProcess處理與瀏覽器中對應的RenderProcessHost的通訊。每一個渲染進程就有惟一的一個RenderProcess對象。這就是全部瀏覽器-渲染器之間的交互發生的方式。

  • RenderView對象與它在瀏覽器進程中對應的RenderViewHost和咱們的webkit嵌入層通訊(經過RenderProcess)。這個對象表明了一個網頁在標籤頁或一個彈出窗口的內容。

在瀏覽器進程中:

  • Browser對象表明了頂級瀏覽器窗口
  • RenderProcessHost對象表明了瀏覽器端瀏覽器的與渲染器的IPC鏈接。在瀏覽器進程裏,每一個渲染進程有一個RenderProcessHost對象。
  • RenderViewHost對象封裝了與遠端瀏覽器的交流,RenderWidgetHost處理輸入並在瀏覽器中爲RenderWidget進行繪製。

想要獲得更多關於這種嵌入是如何工做的詳細信息,能夠查看How Chromium displays web pages design document

共享繪製器進程

一般,每一個新的window或標籤頁是在一個新進程裏打開的。瀏覽器會生成一個新的進程,而後指導它去建立一個RenderView

有時候,有這樣一種必要或慾望在標籤頁或窗口間共享渲染進程。一個web應用程序會在指望同步交流時,打開一個新的窗口,好比,在javascript裏使用window.open。這種狀況下,當咱們建立一個新的window或標籤頁時,咱們須要重用打開這個window的進程。咱們也有一些策略來把新的標籤頁分配的已有的進程(若是總的進程數太大的話,或者若是用戶已經爲這個域名打開了一個進程)。這些策略在Process Models裏也有闡述。

檢測crash或者失誤的渲染

每一個到瀏覽器進程的IPC鏈接會觀察進程句柄。若是這些句柄是signaled(有信號的),那麼渲染進程已經掛了,標籤頁會獲得一個通知。從這時開始,咱們會展現一個「sad tab」畫面來通知用戶渲染器已經掛掉了。這個頁面能夠按刷新按鈕或者經過打開一個新的導航來從新加載。這時,咱們會注意到沒有對應的進程,而後建立一個新的。

渲染器中的沙箱

給定的WebKit是運行在獨立的進程中的,因此咱們有機會限制它對系統資源的訪問。例如,咱們能夠確保渲染器惟一的網絡權限是經過它的父瀏覽器進程實現。類似的,咱們能夠限制它對文件系統的訪問權限來使用host操做系統內置的權限。

除了限制渲染器對文件系統和網絡的訪問權限,咱們也能夠限制它對用戶的顯示器以及相關的東西的一些權限。咱們在獨立的windows桌面(對用戶不可見)中運行每一個進程。這避免了讓渲染器在新的標籤頁或捕捉按鍵之間妥協。

歸還內存

讓渲染器運行在獨立的進程中,賦予隱藏的標籤頁更低的優先級會更加直接。一般,Windows平臺上的最小化的進程會把它們的內存自動房東一個「可用內存」池裏。在低內存的狀況下,Windows會在交換這部份內存到更高優先級內存前,把它們交換到磁盤,以保證用戶可見的程序更易響應。咱們能夠對隱藏的標籤頁使用相同的策略。當渲染器進程沒有頂層標籤頁時,咱們能夠釋放進程的「工做集」空間,做爲一個給系統的信號,讓它若是必要的話,優先把這些內存交換到磁盤。由於咱們發現,當用戶在兩個標籤頁間切換時,減小工做集大小也會減小標籤頁切換性能,因此咱們是逐漸釋放這部份內存的。這意味着若是用戶切換回最近使用的標籤頁,這個標籤頁的內存比最近較少訪問的標籤頁更可能被換入。有着足夠內存的用戶運行他們全部的程序時根本不會注意到這個進程:事實上Windows只會在須要的時候從新聲明這塊數據,因此在有充份內存時,不會有性能瓶頸。

這能幫助咱們在低內存的狀況下獲得最佳的內存軌跡。幾乎不被使用的後臺標籤頁相關的內存能夠被徹底交換掉,前臺標籤頁的數據能夠被徹底加載進內存。相反的,一個單進程瀏覽器會在它的內存裏隨機分配全部標籤頁的數據,而且不可能如此清晰地隔離已使用的和未使用的數據,致使了內存和性能上的浪費。

插件

Firefox風格的NPAPI插件運行在他們本身的進程裏,與渲染器隔離。這會在Plugin Architecture中描述。

如何添加新特性(不用擴充RenderView/RenderViewHost/WebContents)

問題

過去,新的特性(好比,自動填充選取樣例)能夠經過把新特性的代碼導入到RenderView類(在渲染器進程裏)和RenderViewHost類(在瀏覽器進程裏)。若是一個新的特性是在瀏覽器進程的IO線程裏處理的,那麼它的IPC信息由BrowserMessageFilter調度。RenderViewHost會只爲了調用WebContent對象進程調用IPC信息,這會調用另外一塊代碼。全部的瀏覽器與渲染器之間的IPC信息會被聲明在一個巨大的render_messages_internal.h裏,爲每一個新特性修改全部的這些文件意味着這些類會變得臃腫。

解決方案

咱們增長了helper類和對上面的每一個線程IPC信息的過濾的機制。這使得編寫自洽的特性更加容易。

渲染器端

若是你想要過濾和發送IPC信息,實現RenderViewObserver接口(content/renderer/render_view_observer.h)。RenderViewObserver基類持有一個RenderView類,管理對象的生命週期,使其綁定到RenderView(它是可重寫的)。這個類就能夠過濾和發送IPC消息,此外還能夠得到許多特性須要的關於頁面加載與關閉的通知。做爲一個例子,能夠查看ChromeExtensionHelper (chrome/renderer/extensions/chrome_extension_helper.h)。

若是你的特性有一部分代碼是在WebKit內的,避免經過WebViewClient接口回調,這樣咱們就不會使得WebViewClient變得龐大。考慮建立新的WebKit接口給WebKit代碼調用,讓渲染器端的類去實現它。做爲一個例子,查看WebAutoFillClient (WebKit/chromium/public/WebAutoFillClient.h).

瀏覽器UI線程

WebContentsObserver (content/public/browser/web_contents_observer.h)接口容許UI線程的對象過濾IPC信息,以及給出關於頁面導航的通知。做爲一個例子:查看TabHelper (chrome/browser/extensions/tab_helper.h)。

瀏覽器其餘線程

爲了過濾和發送IPC信息給其餘的瀏覽器線程,好比IO/FILE/WEBKIT等等,實現BrowserMessageFilter接口(content/browser/browser_message_filter.h)。BrowserRenderProcessHost對象在它的CreateMessageFilters函數裏創造和增長過濾器。

一般,若是一個特性有許多IPC消息,這些消息應該移動到一個獨立的文件(例如,不要加到render_messages_internal.h裏)。這對過濾線程(除了IO線程)也有幫助。做爲一個例子,查看content/common/pepper_file_messages.h。這容許他們的過濾器PepperFileMessageFilter方便的發送文件到file線程,而不用在不少位置指定它們的ID。

void PepperFileMessageFilter::OverrideThreadForMessage(
    const IPC::Message& message,
    BrowserThread::ID* thread) {
  if (IPC_MESSAGE_CLASS(message) == PepperFileMsgStart)
    *thread = BrowserThread::FILE;
}
相關文章
相關標籤/搜索