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執行緒的訊息迴圈進行。這符合我們在前面
我們分析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類的建構函式的實現如下所示:
- RenderViewHostImpl::RenderViewHostImpl(
- SiteInstance* instance,
- RenderViewHostDelegate* delegate,
- RenderWidgetHostDelegate* widget_delegate,
- int routing_id,
- int main_frame_routing_id,
- bool swapped_out,
- bool hidden)
- : RenderWidgetHostImpl(widget_delegate,
- instance->GetProcess(),
- routing_id,
- hidden),
- ...... {
- ......
- }
這裡我們主要關注型別為SiteInstance的引數instance,它指向的實際上是一個SiteInstanceImpl物件,用來描述Chromium當前載入的一個網站例項。RenderViewHostImpl類的建構函式呼叫該SiteInstanceImpl物件的成員函式GetProcess獲得一個RenderProcessHostImpl物件,如下所示:
- RenderProcessHost* SiteInstanceImpl::GetProcess() {
- ......
- // Create a new process if ours went away or was reused.
- if (!process_) {
- BrowserContext* browser_context = browsing_instance_->browser_context();
- // If we should use process-per-site mode (either in general or for the
- // given site), then look for an existing RenderProcessHost for the site.
- bool use_process_per_site = has_site_ &&
- RenderProcessHost::ShouldUseProcessPerSite(browser_context, site_);
- if (use_process_per_site) {
- process_ = RenderProcessHostImpl::GetProcessHostForSite(browser_context,
- site_);
- }
- // If not (or if none found), see if we should reuse an existing process.
- if (!process_ && RenderProcessHostImpl::ShouldTryToUseExistingProcessHost(
- browser_context, site_)) {
- process_ = RenderProcessHostImpl::GetExistingProcessHost(browser_context,
- site_);
- }
- // Otherwise (or if that fails), create a new one.
- if (!process_) {
- if (g_render_process_host_factory_) {
- process_ = g_render_process_host_factory_->CreateRenderProcessHost(
- browser_context, this);
- } else {
- StoragePartitionImpl* partition =
- static_cast<StoragePartitionImpl*>(
- BrowserContext::GetStoragePartition(browser_context, this));
- process_ = new RenderProcessHostImpl(browser_context,
- partition,
- site_.SchemeIs(kGuestScheme));
- }
- }
- ......
- }
- ......
- return process_;
- }
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,如下所示:
- RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
- RenderProcessHost* process,
- int routing_id,
- bool hidden)
- : ......,
- process_(process),
- ...... {
- ......
- }
引數process指向的RenderProcessHostImpl物件儲存在RenderWidgetHostImpl類的成員變數process_中,以後就可以通過RenderWidgetHostImpl類的成員函式GetProcess獲得該RenderProcessHostImpl物件,如下所示:
- RenderProcessHost* RenderWidgetHostImpl::GetProcess() const {
- return process_;
- }
有了RenderProcessHostImpl之後,接下來我們就開始分析RenderViewHostImpl類的成員函式CreateRenderView建立一個新的Render程序的過程了,如下所示:
- bool RenderViewHostImpl::CreateRenderView(
- const base::string16& frame_name,
- int opener_route_id,
- int proxy_route_id,
- int32 max_page_id,
- bool window_was_created_with_opener) {
- ......
- if (!GetProcess()->Init())
- returnfalse;
- ......
- }
這個函式定義在檔案external/chromium_org/content/browser/renderer_host/render_view_host_impl.cc中。
RenderViewHostImpl類的成員函式CreateRenderView首先呼叫從父類RenderWidgetHostImpl繼承下來的成員函式GetProcess獲得一個RenderProcessHostImpl物件,接著再呼叫該RenderProcessHostImpl物件的成員函式Init檢查是否需要為當前載入的網頁建立一個新的Render程序。
RenderProcessHostImpl類的成員函式Init的實現如下所示:
- bool RenderProcessHostImpl::Init() {
- // calling Init() more than once does nothing, this makes it more convenient
- // for the view host which may not be sure in some cases
- if (channel_)
- returntrue;
-
相關推薦
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的初始化是
WebKit之Chromium的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 & 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