Chromium學習筆記:程式啟動入口分析(Windows)
Chromium學習筆記:程式啟動入口分析(Windows)
以下筆記內容均為Windows版本。本篇筆記跟蹤記錄了Chromium的啟動過程,主要關注Browser
程序和Renderer
程序。根據Chromium
專案的分層設計,我們把Content API
稱作為Content
層,而把呼叫Content API
實現瀏覽器程式的部分稱作為Embedder
層。在專案中,Embedder
層有chrome
、content_shell
等多種實現。
1、main() 函式
Chromium的main函式在chrome\app\chrome_exe_main_win.cc
,具體如下:
// chrome\app\chrome_exe_main_win.cc
|
在main函式中,最重要的一步,就是int rc = loader->Launch(instance, exe_entry_point_ticks);
載入chrome.dll執行
。
2、載入 chrome.dll
在這裡首先呼叫了MakeMainDllLoader()
函式,這是一個靜態函式,在chrome\app\main_dll_loader.cc
中,內容如下:
// chrome\app\main_dll_loader.cc
|
函式建立並返回一個ChromiumDllLoader
,緊接著再呼叫它的Launch
函式,內容如下:
// chrome\app\main_dll_loader.cc
|
這裡完成了chrome.dll
的載入,並且執行裡面的ChromeMain
函式。
3、ChromeMain() 函式
ChromeMain
函式負責Embedder
層的實現類建立,並傳遞給Content
層,定義在chrome\app\chrome_main.cc
中,內容如下:
// chrome\app\chrome_main.cc
|
在ChromeMain中,最終執行到了content::ContentMain
這個函式。
4、content::ContentMain() 函式
程式碼執行到這裡,進入了Content
層,並且傳入引數content::ContentMainParams
型別的引數params
,它是由Embedder
層傳遞過來的重要引數,裡面包含了Embedder
層的具體實現資訊,此結構體在content\public\app\content_main.h
中定義如下:
// content\public\app\content_main.h
|
其中有一個重要的成員變數delegate
,其型別為content::ContentMainDelegate
,它在content\public\app\content_main_delegate.cc
中定義如下:
// content\public\app\content_main_delegate.cc
|
可以看到,這裡定義了一系列與啟動相關的操作,並且通過幾個CreateXXX
的函式,獲取ContentBrowserClient
、ContentRendererClient
等介面具體的實現,這也是content API
的巧妙設計,通過這種方式,將瀏覽器的實現放入了content
中。
繼續往下看,content::ContentMain()
中呼叫了content\app\content_main.cc
中的service_manager::Main()
:
// content\app\content_main.cc
|
在這裡,使用一個content::ContentServiceManagerMainDelegate
物件來構建了main_params
,並傳入了service_manager::Main()
。
5、service_manager::Main 函式
service_manager::Main
函式位於services\service_manager\embedder\main.cc
,接收一個MainParams
型別的引數,具體如下:
// services\service_manager\embedder\main.cc
|
這裡擷取的程式碼比較長,也非常重要,我們主要關注這四個部分:
- 根據傳入的
delegate
和command_line
決定程序的型別 - 執行環境的初始化,比如
CreateATLModuleIfNeeded
,SetupCRT
並用is_initialized
來防止重複執行 - 通過傳入的
delegate
進行程式的初始化操作,delegate->Initialize(init_params)
- 根據程序型別啟動相應的工作
這裡的delegate
型別為service_manager::MainDelegate*
,是在services/service_manager/embedder/main_delegate.h
中定義的抽象類,在這裡我們主要關注它的Initialize
、RunEmbedderProcess
和ShutDownEmbedderProcess
,其中Initialize
為被宣告為純虛擬函式,RunEmbedderProcess
和ShutDownEmbedderProcess
又是什麼都不做的,程式碼如下:
// services/service_manager/embedder/main_delegate.h
|
// services/service_manager/embedder/main_delegate.cc
|
回到service_manager::Main()
,我們看到第一句MainDelegate* delegate = params.delegate;
中的params.delegate
就是前面在content::ContentMain
中構建main_params
所使用的content::ContentServiceManagerMainDelegate
物件,因此,上述的三個函式Initialize
、RunEmbedderProcess
、ShutDownEmbedderProcess
是由ContentServiceManagerMainDelegate
來最終實現的,來看程式碼:
// content\app\content_service_manager_main_delegate.cc
|
在這三個函式的定義中,都使用了content_main_runner_
這個成員變數來具體執行,它的定義為std::unique_ptr<ContentMainRunnerImpl>
。
6、整個程式的Runner,content::ContentMainRunnerImpl
這個content::ContentMainRunnerImpl
是content::ContentMainRunner
介面的一個實現,先來看介面的宣告:
// content\app\content_main_runner_impl.h
|
再來看實現類的程式碼:
// content\app\content_main_runner_impl.h
|
7、ContentMainRunner::Initialize() 函式
先來看Initialize
函式:
// content\app\content_main_runner_impl.cc
|
大致看一下,在這個Initialize
中,主要是根據command_line
啟動了相應的sandbox service
,並在啟動前後都觸發了delegate_->PreSandboxStartup()
和delegate_->SandboxInitialized(process_type)
,這個delegate_
來自於傳入的content::ContentMainParams
結構體,這個結構體是在chrome_main.cc
中呼叫content::ContentMain(params)
時所建立,所以這個delegate_
正是前面所提到的巧妙設計中,繼承自content::ContentMainDelegate
的ChromeMainDelegate
物件,通過這一系列的呼叫,content
層就把建立sandbox service
前後的事件觸發了出來,具體實現者只要在ChromeMainDelegate
中填充這兩個時間點要做的事即可。
8、程序入口,ContentMainRunner::Run() 函式
再來看Run
函式:
// // content\app\content_main_runner_impl.cc
|
此處先判斷process_type
是否為空,為空則代表當前執行的是預設程序(一般情況下為Browser
程序),則呼叫RunServiceManager()
,否則呼叫RunOtherNamedProcessTypeMain
根據process_type
來執行相應的程序。先來看RunServiceManager
:
// content\app\content_main_runner_impl.cc
|
同樣,這裡通過delegate_
做了一些操作之後,最後呼叫了RunBrowserProcessMain()
函式,內容如下:
// content\app\content_main_runner_impl.cc
|
非常簡單明瞭,首先通過delegate->RunProcess
把執行預設程序的優先權交由Embedder
層,如果Embedder
層成功執行了程序並最終返回了成功標誌(exit_code >= 0
),那麼就退出函式;如果Embedder
層對預設程序沒有定義,就繼續執行content::BrowserMain
,由此,Browser
程序開始執行。
再來看RunOtherNamedProcessTypeMain
函式:
// content\app\content_main_runner_impl.cc
|
先建立了一個程序型別和入口函式指標的對應陣列,再根據程序型別去具體執行,執行的過程與Browser
程序一樣,先通過delegate->RunProcess
交由Embedder
層處理,如果未處理再呼叫預設的程序入口函式,可以看到分別提供了UtilityMain
、RendererMain
、GpuMain
這三個程序的入口,其中RendererMain
則是我們關注的Renderer
程序的入口函式,Renderer
程序從此處開始執行。最後一句,如果程序型別不在以上範圍內,則交由Embedder
去處理。
9、程式結束
void ContentMainRunnerImpl::Shutdown() {
|
首先通過delegate_->ProcessExiting(process_type)
通知Embedder
層處理,然後做了一些善後釋放的工作,最後將is_shutdown_
標記置為true
。
10、總結
前面分析了這麼多,其實結合類圖來看一下還是很簡單明瞭的,主要起到作用的就是圖中標紅的三個,service_manager::Main
通過content::ContentServiceManagerMainDelegate
的例項呼叫了content::ContentMainRunnerImpl
例項中的Initialize()
、Run()
、Shutdown()
函式,而在這個Runner中,又通過content::ContentMainDelegate
介面指標呼叫到了由Embedder
層建立的ChromeMainDelegate
例項中的函式,由此完成了程式的啟動以及Content
層對Embedder
的互動。