1. 程式人生 > >WebKit之Chromium的Render程序分析

WebKit之Chromium的Render程序分析

配置多程序的情況下,Chromium的網頁渲染和JS執行在一個單獨的程序中進行。這個程序稱為Render程序,由Browser程序啟動。在Android平臺中,Browser程序就是Android應用程式的主程序,而Render程序就是Android應用程式的Service程序,它們通過UNIX Socket進行通訊。本文就詳細分析Chromium的Browser程序啟動Render程序的過程。

       Render程序啟動完成之後,將與Browser程序建立以下的IPC通道,如圖1所示:


圖1 Browser程序與Render程序的IPC通訊過程

       在Browser程序中,一個RenderProcessHost物件用來描述它所啟動的一個Render程序,而一個RenderViewHost物件用來描述執行在一個Render程序中的一個網頁,我們可以將它理解為瀏覽器中的一個TAB。這兩個物件在Render程序中都有一個對等體,它們分別是一個RenderProcess物件和一個RenderView物件。這裡說的對等體,就是它們是Browser程序和Render程序進行IPC的兩個端點,類似於TCP/IP網路堆疊中的層對層通訊。例如,RenderViewHost和RenderView之間的IPC通訊,就代表了Browser程序請求Render程序載入、更新和渲染一個網頁。

       RenderViewHost和RenderView之間的IPC通訊,實際上是通過一個UNIX Socket進行的。這個UNIX Socket的兩端分別被封裝為兩個Channel物件,分別執行在Browser程序和Render程序各自的IO執行緒中。這樣RenderViewHost和RenderView之間的IPC通訊就要通過上述的兩個Channel物件進行。

       在Browser程序中,由於RenderViewHost物件執行在主執行緒中,因此當它需要請求執行在IO執行緒中的Channel物件執行一次IPC時,就要通過IO執行緒的訊息迴圈進行。這符合我們在前面

Chromium多執行緒模型設計和實現分析一文中提到的Chromium的多執行緒設計哲學:每一個物件都只執行在一個執行緒中,物件之間需要通訊時就通過訊息迴圈進行。同樣,在Render程序中,由於RenderView物件執行在Render執行緒中,因此當Render程序的Channel物件接收一個來自Browser程序的RenderViewHost物件的IPC訊息時,需要通過Render執行緒的訊息迴圈將IPC訊息轉發給RenderView進行處理。從RenderView物件到RenderViewHost物件的通訊過程也是類似的。

       我們分析Render程序的啟動過程,目的就是為了能夠理解Browser程序和Render程序是如何建立IPC通道的,因為以後Browser程序與Render程序的互動和協作,都是通過這個IPC通道進行的。為此,我們在分析Render程序的啟動過程中,將著重分析圖1涉及到的各個物件的初始過程。

       我們注意到,執行在Browser程序中的通訊物件是以Host結尾的,而在執行在Render程序中的對等通訊物件,則是沒有Host結尾的,因此當我們Chromium的原始碼中看到一個物件的型別時,就可以推斷出該物件執行在哪個程序中。

       事實上,RenderProcessHost、RenderViewHost、RenderProcess和RenderView僅僅是定義了一個抽象介面,真正用來執行IPC通訊的物件,是實現了上述抽象介面的一個實現者物件,這些實現者物件的型別以Impl結尾,因此,RenderProcessHost、RenderViewHost、RenderProcess和RenderView對應的實現者物件的型別就分別為RenderProcessHostImpl、RenderViewHostImpl、RenderProcessImpl和RenderViewImpl。

       為了更好地理解Render程序的啟動過程,我們有必要了解上述Impl物件的類關係圖。

       RenderViewHostImpl物件的類關係圖如下所示:


圖2 RenderViewHostImpl類關係圖

       RenderViewHostImpl類多重繼承了RenderViewHost類和RenderWidgetHostImpl類,後面這兩個類又有一個共同的虛基類RenderWidgetHost,該虛基類又實現了一個Sender介面,該介面定義了一個重要的成員函式Send,用來執行IPC通訊。

       RenderWidgetHostImpl類還實現了一個Listener介面,該介面定義了兩個重要的成員函式OnMessageReceived和OnChannelConnected。前者用來接收IPC訊息並且進行分發,後者用來在IPC通道建立時執行一些初始化工作。

       實際上,當RenderViewHostImpl類需要發起一次IPC時,它是通過父類RenderWidgetHostImpl的成員變數process_指向的一個RenderProcessHost介面進行的。該RenderProcessHost介面指向的實際上是一個RenderProcessHostImpl物件,它的類關係圖如圖3所示:


圖3 RenderProcessHostImpl類關係圖

       RenderProcessHostImpl類實現了RenderProcessHost介面,後者又多重繼承了Sender和Listener類。

       RenderProcessHostImpl類有一個成員變數channel_,它指向了一個ChannelProxy物件。ChannelProxy類實現了Sender介面,RenderProcessHostImpl類就是通過它來發送IPC訊息的。

       ChannelProxy類有一個成員變數context_,它指向了一個ChannelProxy::Context物件。ChannelProxy::Context類實現了Listener介面,因此它可以用來接收IPC訊息。ChannelProxy類就是通過ChannelProxy::Context類來發送和接收IPC訊息的。

       ChannelProxy::Context類有一個型別為Channel的成員變數channel_,它指向的實際上是一個ChannelPosix物件。ChannelPosix類繼承了Channel類,後者又實現了Sender介面。ChannelProxy::Context類就是通過ChannelPosix類傳送IPC訊息的。

       繞了一圈,總結來說,就是RenderProcessHostImpl類是分別通過ChannelPosix類和ChannelProxy::Context類來發送和接收IPC訊息的。

       上面分析的RenderViewHostImpl物件和RenderProcessHostImpl物件都是執行在Browser程序的,接下來要分析的RenderViewImpl類和RenderProcessImpl類是執行在Render程序的。

        RenderViewImpl物件的類關係圖如下所示:


圖4 RenderViewImpl類關係圖

       RenderViewImpl類多重繼承了RenderView類和RenderWidget類。RenderView類實現了Sender介面。RenderWidget類也實現了Sender介面,同時也實現了Listener介面,因此它可以用來發送和接收IPC訊息。

       RenderWidget類實現了介面Sender的成員函式Send,RenderViewImpl類就是通過它來發送IPC訊息的。RenderWidget類的成員函式Send又是通過一個用來描述Render執行緒的RenderThreadImpl物件來發送IPC類的。這個RenderThreadImpl物件可以通過呼叫RenderThread類的靜態成員函式Get獲得。

       RenderThreadImpl物件的類關係圖如下所示:

圖5 RenderThreadImpl類關係圖

       RenderThreadImpl類多重繼承了RenderThread類和ChildThread類。RenderThread類實現了Sender介面。ChildThread類也實現Sender介面,同時也實現了Listener介面,因此它可以用來發送和接收IPC訊息。

       ChildThread類有一個成員變數channel_,它指向了一個SyncChannel物件。SyncChannel類繼承了上面提到的ChannelProxy類,因此,ChildThread類通過其成員變數channel_指向的SyncChannel物件可以傳送IPC訊息。

       從上面的分析又可以知道,ChannelProxy類最終是通過ChannelPosix類傳送IPC訊息的,因此總結來說,就是RenderThreadImpl是通過ChannelPosix類傳送IPC訊息的。

       接下來我們再來看RenderProcessImpl物件的類關係圖,如下所示:


圖6  RenderProcessImpl類關係圖

       RenderProcessImpl類繼承了RenderProcess類,RenderProcess類又繼承了ChildProcess類。ChildProcess類有一個成員變數io_thread_,它指向了一個Thread物件。該Thread物件描述的就是Render程序的IO執行緒。

       有了上面的基礎知識之後,接下來我們開始分析Render程序的啟動過程。我們將Render程序的啟動過程劃分為兩部分。第一部分是在Browser程序中執行的,它主要負責建立一個UNIX Socket,並且將該UNIX Socket的Client端描述符傳遞給接下來要建立的Render程序。第二部分是在Render程序中執行的,它負責執行一系列的初始化工作,其中之一就是將Browser程序傳遞過來的UNIX Socket的Client端描述符封裝在一個Channel物件中,以便以後可以通過它來和Browser程序執行IPC。

      Render程序啟動過程的第一部分子過程如下所示:


圖7 Render程序啟動的第一部分子過程

      圖7列出的僅僅是一些核心過程,接下來我們通過程式碼來分析這些核心過程。

      我們首先了解什麼情況下Browser程序會啟動一個Render程序。當我們在Chromium的位址列輸入一個網址,然後進行載入的時候,Browser程序經過判斷,發現需要在一個新的Render程序中渲染該網址的內容時,就會建立一個RenderViewHostImpl物件,並且呼叫它的成員函式CreateRenderView觸發啟動一個新的Render程序。後面我們分析WebView載入一個URL的時候,就會看到觸發建立RenderViewHostImpl物件的流程。

      RenderViewHostImpl物件的建立過程,即RenderViewHostImpl類的建構函式的實現如下所示:

  1. RenderViewHostImpl::RenderViewHostImpl(  
  2.     SiteInstance* instance,  
  3.     RenderViewHostDelegate* delegate,  
  4.     RenderWidgetHostDelegate* widget_delegate,  
  5.     int routing_id,  
  6.     int main_frame_routing_id,  
  7.     bool swapped_out,  
  8.     bool hidden)  
  9.     : RenderWidgetHostImpl(widget_delegate,  
  10.                            instance->GetProcess(),  
  11.                            routing_id,  
  12.                            hidden),  
  13.       ...... {  
  14.   ......  
  15. }  
      這個函式定義在檔案external/chromium_org/content/browser/renderer_host/render_view_host_impl.cc中。

      這裡我們主要關注型別為SiteInstance的引數instance,它指向的實際上是一個SiteInstanceImpl物件,用來描述Chromium當前載入的一個網站例項。RenderViewHostImpl類的建構函式呼叫該SiteInstanceImpl物件的成員函式GetProcess獲得一個RenderProcessHostImpl物件,如下所示:

  1. RenderProcessHost* SiteInstanceImpl::GetProcess() {  
  2.   ......  
  3.   // Create a new process if ours went away or was reused.
  4.   if (!process_) {  
  5.     BrowserContext* browser_context = browsing_instance_->browser_context();  
  6.     // If we should use process-per-site mode (either in general or for the
  7.     // given site), then look for an existing RenderProcessHost for the site.
  8.     bool use_process_per_site = has_site_ &&  
  9.         RenderProcessHost::ShouldUseProcessPerSite(browser_context, site_);  
  10.     if (use_process_per_site) {  
  11.       process_ = RenderProcessHostImpl::GetProcessHostForSite(browser_context,  
  12.                                                               site_);  
  13.     }  
  14.     // If not (or if none found), see if we should reuse an existing process.
  15.     if (!process_ && RenderProcessHostImpl::ShouldTryToUseExistingProcessHost(  
  16.             browser_context, site_)) {  
  17.       process_ = RenderProcessHostImpl::GetExistingProcessHost(browser_context,  
  18.                                                                site_);  
  19.     }  
  20.     // Otherwise (or if that fails), create a new one.
  21.     if (!process_) {  
  22.       if (g_render_process_host_factory_) {  
  23.         process_ = g_render_process_host_factory_->CreateRenderProcessHost(  
  24.             browser_context, this);  
  25.       } else {  
  26.         StoragePartitionImpl* partition =  
  27.             static_cast<StoragePartitionImpl*>(  
  28.                 BrowserContext::GetStoragePartition(browser_context, this));  
  29.         process_ = new RenderProcessHostImpl(browser_context,  
  30.                                              partition,  
  31.                                              site_.SchemeIs(kGuestScheme));  
  32.       }  
  33.     }  
  34.     ......  
  35.   }  
  36.   ......  
  37.   return process_;  
  38. }  
       這個函式定義在檔案external/chromium_org/content/browser/site_instance_impl.cc中。

       SiteInstanceImpl物件的成員變數process_是一個RenderProcessHost指標,當它的值等於NULL的時候,就表示Chromium還沒有為當前正在處理的一個SiteInstanceImpl物件建立過Render程序,這時候就需要建立一個RenderProcessHostImpl物件,並且儲存在成員變數process_中,以及返回給呼叫者,以便呼叫者接下來可以通過它啟動一個Render程序。另一方面,如果SiteInstanceImpl物件的成員變數process_已經指向了一個RenderProcessHostImpl物件,那麼就直接將該RenderProcessHostImpl物件返回給呼叫者即可。

       注意上述RenderProcessHostImpl物件的建立過程:

       1. 如果Chromium啟動時,指定了同一個網站的所有網頁都在同一個Render程序中載入,即本地變數use_process_per_site的值等於true,那麼這時候SiteInstanceImpl類的成員函式GetProcess就會先呼叫RenderProcessHostImpl類的靜態函式GetProcessHostForSite檢查之前是否已經為當前正在處理的SiteInstanceImpl物件描述的網站建立過Render程序。如果已經建立過,那麼就可以獲得一個對應的RenderProcessHostImpl物件。

       2. 如果按照上面的方法找不到一個相應的RenderProcessHostImpl物件,本來就應該要建立一個新的Render程序了,也就是要建立一個新的RenderProcessHostImpl物件了。但是由於當前建立的Render程序已經超出預設的最大數量了,這時候就要複用前面已經啟動的Render程序,即使這個Render程序載入的是另一個網站的內容。

       3. 如果通過前面兩步仍然找不到一個對應的RenderProcessHostImpl物件,這時候就真的是需要建立一個RenderProcessHostImpl物件了。取決於SiteInstanceImpl類的靜態成員變數g_render_process_host_factory_是否被設定,建立一個新的RenderProcessHostImpl物件的方式有所不同。如果該靜態成員變數被設定了指向一個RenderProcessHostFactory物件,那麼就呼叫該RenderProcessHostFactory物件的成員函式CreateRenderProcessHost建立一個從RenderProcessHost類繼承下來的子類物件。否則的話,就直接建立一個RenderProcessHostImpl物件。

       這一步執行完成後,回到RenderViewHostImpl類的建構函式中,從這裡返回的RenderProcessHostImpl物件用來初始化RenderViewHostImpl類的父類RenderWidgetHostImpl,如下所示:

  1. RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,  
  2.                                            RenderProcessHost* process,  
  3.                                            int routing_id,  
  4.                                            bool hidden)  
  5.     : ......,  
  6.       process_(process),  
  7.       ...... {  
  8.   ......  
  9. }  
      這個函式定義在檔案external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc中。

      引數process指向的RenderProcessHostImpl物件儲存在RenderWidgetHostImpl類的成員變數process_中,以後就可以通過RenderWidgetHostImpl類的成員函式GetProcess獲得該RenderProcessHostImpl物件,如下所示:

  1. RenderProcessHost* RenderWidgetHostImpl::GetProcess() const {  
  2.   return process_;  
  3. }  
      這個函式定義在檔案external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc中。

      有了RenderProcessHostImpl之後,接下來我們就開始分析RenderViewHostImpl類的成員函式CreateRenderView建立一個新的Render程序的過程了,如下所示:

  1. bool RenderViewHostImpl::CreateRenderView(  
  2.     const base::string16& frame_name,  
  3.     int opener_route_id,  
  4.     int proxy_route_id,  
  5.     int32 max_page_id,  
  6.     bool window_was_created_with_opener) {  
  7.   ......  
  8.   if (!GetProcess()->Init())  
  9.     returnfalse;  
  10.   ......  
  11. }  

       這個函式定義在檔案external/chromium_org/content/browser/renderer_host/render_view_host_impl.cc中。

       RenderViewHostImpl類的成員函式CreateRenderView首先呼叫從父類RenderWidgetHostImpl繼承下來的成員函式GetProcess獲得一個RenderProcessHostImpl物件,接著再呼叫該RenderProcessHostImpl物件的成員函式Init檢查是否需要為當前載入的網頁建立一個新的Render程序。

       RenderProcessHostImpl類的成員函式Init的實現如下所示:

  1. bool RenderProcessHostImpl::Init() {  
  2.   // calling Init() more than once does nothing, this makes it more convenient
  3.   // for the view host which may not be sure in some cases
  4.   if (channel_)  
  5.     returntrue;  
  6. 相關推薦

    WebKit影象顯示分析流程

    ## 流程分析 <img src="a.jpg"> ## 影象繪製分析 ImageFrame::setPixmap(const QPixmap & pixmap={...}) ImageDecoderQt::internalHandleCurrentI

    android6.0原始碼分析Zygote程序分析

    在android6.0原始碼分析之Runtime的初始化一文中,對Zygote程序的初期的Runtime初始化過程進行了分析,在Runtime啟動結束後,會對Zygote程序進行初始化,其它Java程序都需要從Zygote程序來fork,而Zygote的初始化是

    WebKitChromium的Render程序分析

    配置多程序的情況下,Chromium的網頁渲染和JS執行在一個單獨的程序中進行。這個程序稱為Render程序,由Browser程序啟動。在Android平臺中,Browser程序就是Android應用程式的主程序,而Render程序就是Android應用程式的Serv

    一個利用“永恒藍”漏洞傳播的挖礦程序分析

    amp 不同 數據文件 緩解 項目 近日 相同 oos 參數 背景介紹 近日,漁村安全團隊追蹤到一個利用永恒之藍漏洞傳播的挖礦程序,其具備高度的模塊化和較強的傳播能力,在短短數日就感染了數萬臺用戶電腦。針對該突發情況,漁村安全團隊迅速組織應急工作,最終使得目前的感染情況受到

    Spark提交應用程序Spark-Submit分析

    需要 使用 please requested 建議 eas -m rfs export 1.提交應用程序 在提交應用程序的時候,用到 spark-submit 腳本。我們來看下這個腳本: if [ -z "${SPARK_HOME}" ]; then export

    linux usb列舉過程分析守護程序及其喚醒

    usb熱插拔,即usb裝置可以實現即插即用,像U盤一樣,插到電腦裡就可以用,不用時可以直接拔除,這個動作不會影響USB裝置使用效能。        在linx 系統中,usb熱插拔由兩部分方面共同實現,即核心空間和使用者空間,核心由一個守護程序實現,使用者空間由udev 程式

    TS流代碼分析

    xtra new 校正 reat ted 跟著 ror enable 好的   代碼分析前,先要了解TS流基本概念:TS流之基本概念。   VLC解析TS流是通過libts庫來分離的,libts庫使用libdvbpsi庫來解TS表。 1. libts庫在加載的時候,會將以下

    排隊理論性能分析 - Little Law &amp; Utilization Law

    util ive community target 等待時間 時間 說明 div rman 了解一個系統的性能一般是參考一些度量值(Metric),而怎樣計算出這些Metric就是我們要討論的。Little Law(排隊理論:利特兒法則)和Utilization L

    hdu 5253 連接的管道(kruskal)(2015年百度程序設計大賽 - 初賽(2))

    ostream turn targe 告訴 margin 表輸入 輸入 cst 代碼 連接的管道 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) T

    2014年百度程序設計大賽 - 資格賽 1002 Disk Schedule(雙調歐幾裏得旅行商問題)

    problem code 數據讀取 包括 想是 tracking sample cout http Problem Description 有非常多從磁盤讀取數據的需求。包含順序讀取、隨機讀取。為了提高效率,須要人為安排磁盤讀取。然而,在現實中。這樣的做法非常復雜。

    2014年百度程序設計大賽 - 資格賽 1001 Energy Conversion

    大賽 pro 整數 code clu n) 足夠 gin bre Energy Conversion Problem Description   魔法師百小度也有遇到難題的時候——   如今,百小度正在一個古老的石門面前,石門上有一段古老的魔法文字,讀懂這樣的魔法

    Elasticsearch學習深入聚合分析三---案例實戰

    引用 實戰 avg buck oba core 電視 針對 過濾 1. 統計指定品牌下每個顏色的銷量 任何的聚合,都必須在搜索出來的結果數據中進行,搜索結果,就是聚合分析操作的scope GET /tvs/sales/_search { "size": 0, "

    Elasticsearch學習深入聚合分析五---案例實戰

    ppi ont doc indices 理解 req eve 同步 nod 1. fielddata核心原理   fielddata加載到內存的過程是lazy加載的,對一個analzyed field執行聚合時,才會加載,而且是field-level加載的,一個index的

    Android 65K問題Multidex原理分析及NoClassDefFoundError的解決方法

    bottom mini ati ... types auto weight right for Android 65K問題相信困惑了不少人,盡管AS的出來能夠通過分dex高速解決65K問題,可是同一時候也easy由於某些代碼沒有打包到MainDex裏

    集群通信組件tribes應用程序處理入口

    app sdn 兩個 https pub src listen 負責 microsoft Tribes為了更清晰更好地劃分職責。它被設計成用IO層和應用層,IO層專心負責網絡傳輸方面的邏輯處理。把接收到的數據往應用層傳送,當然應用層發送的數據也是通過此IO

    程序5】 題目:利用條件運算符的嵌套來完成此題:學習成績>=90分的同學用A表示,60-89分之間的用B表示,60分以下的用C表示。 1.程序分析:(a>b)?a:b這是條件運算符的基本例子。

    window code 例子 prompt 利用 學習 amp text span if…else語句相對比較多,但是容易理解 1 var scroe = window.prompt("請輸入1-100之間的數") 2 scroe = parseInt

    走進STM32世界Hex程序燒寫

    高級功能 出錯 rom from device select oot 點擊 工具 多數51單片機(STC系列單片機)的初學者都知道,在51單片機初上電時,可以通過PC機上位機軟件將程序引導至bootloader,從而將新程序的hex文件下載至單片機中,完成程序的升級或是更新

    Python學習第一章:第一次的親密接觸初識程序(二)

    inpu alt blog put 分享 裏的 hello 格式 img 上次我們講到print(Hello Word)及變量的使用,下面我們繼續: 比如我們想在程序中實現格式化輸出如: 那麽我們我們就要進行簡單的格式化定義,一個很簡單的input語法實現,案例如下:

    中國有嘻哈和極限挑戰大數據分析

    ges 大數據 images 星期六 數據 image 發展 極限 alt 根據中國有嘻哈和極限挑戰的大數據分析,分析得出,在星期日對於極限挑戰網友的網絡點擊率是最高的,正式因為星期日是極限挑戰一周一次的播出時間,進而星期日極限挑戰的網絡點擊率比中國有嘻哈多,而前一日,星

    JFinal源碼 分析 Core包分析

    代碼 types nmap null 可能 tool oca 開發 rep ActionHandler.java   這個類繼承了上面 說的Handler類,首先我們 上 幾個屬性 ,下面幾個 屬性我們 需要 關心哪些東西 呢?首先 是ActionMapping和Rend