一起学习Chromium之Browser进程启动分析
2016-08-10 13:09
2101 查看
前言
从接触Chromium开始,参考过很多网上的分析文章,获益匪浅。本系列文章尝试着从新的角度,逐步分析Chromium的部分关键知识点,希望能分析清楚初学者常常比较困惑的问题,比如Chromium怎么启动,各个进程(Browser/Render/GPU等)怎么启动,Chromium怎么显示一个网页等,进而理顺Chromium中各种类的关系和作用。而作者也会在文章中提出自己尚未理解的问题,希望能达到互动研究共同提高的效果。ContentShell
鉴于Chromium本身的复杂和庞大,从ContentShell来分析是一个比较好的选择。关于Content模块和Chromium的关系,可以参考这里。ContentShell App的启动是从
ContentShellActivity.java的
onCreate(...)开始,这里首先会初始化CommandLine,利用CommandLine可以设置各种启动参数,比如singleprocess,GPU hardware acceleration等,这里详细列出了Chrome中支持的command lines。但这部分和本文主题无关,暂且略过。
接着会调用
LibraryLoader.java的静态函数
get(...)来实例化
LibraryLoader,并指定
LibraryProcessType为
PROCESS_BROWSER。从名字可以看出来,
LibraryLoader的目的就是加载和注册native的libraries。这是通过
ensureInitialized(...)来调用
loadAlreadyLocked(...)和
initializeAlreadyLocked(...)来完成的。在
loadAlreadyLocked(...)中,通过
Linker.loadLibrary(...)或者
System.loadLibrary(...)来触发native层的
JNI_OnLoad(...)。ContentShell对应的native文件是
shell_library_loader.cc,可以看到在
JNI_OnLoad(...)中主要做了两件事情:
Compositor的初始化。
创建并设置
ContentMainDelegate,这里是
ShellMainDelegate.
Compositor的初始化是在
content/browser/renderer_host/compositor_impl_android.cc的
Initialize()完成的,但其实只是设置了变量
g_initialized(意义是什么?)。而
ShellMainDelegate是比较核心的class,后面的分析会多次提到。
现在回到
ContentShellActivity.java,会看到接下来的重点在于
BrowserStartupController。先是通过其静态函数
get(...)来实例化一个class,并设置其
LibraryProcessType为
PROCESS_BROWSER。这和前面的
LibraryLoader一样,都是用了设计模式的单例模式。然后调用其
startBrowserProcessesAsync(...)异步启动和初始化Content模块。
Tips :
startBrowserProcessesAsync(…)和startBrowserProcessesSync(…)的区别:
调用startBrowserProcessesAsync(…)会同时传入参数BrowserStartupController.StartupCallback,使得当Content模块初始化完成后,会调用ContentShellActivity类的成员函数finishInitialization(…)继续执行启动ContentShell APK的其他工作。startBrowserProcessesAsync(…)中会调用prepareToStartBrowserProcess(…),同时new Runnable()作为参数传入,目的是在随后执行contentStart()。
这里需要注意,虽然实现Runnable接口是java中两种实现多线程的方式之一(另一种是继承Thread类),但是Runnable本身和线程无关,只是一个拥有run()方法的Object。所以在
prepareToStartBrowserProcess(...)中,会在资源提取(ResourceExtractor)完成后执行
contentStart()。这里使用的类
ResourceExtractor其实是和浏览器对HTML5多语言的支持有关,暂且略过。
Native层
contentStart()最终是通过JNI调用native层
content/app/android/content_main.cc中的
Start(...)。而之前设置
ShellMainDelegate正是用到了
content_main.cc中的
SetContentMainDelegate(...)。
在
Start(...)中,主要做了下面几件事:
使用前面创建和设置的
ShellMainDelegate初始化
g_content_main_delegate,并初始化结构体
ContentMainParams params的成员变量
delegate。
这里会用到Chromium的LazyInstance,它提供了一种延迟创建全局静态对象的方式,而且是完全的线程安全,
base/lazy_instance.h对其有详细描述和示例。
创建并初始化
Class ContentMainRunner(content/app/content_main_runner.cc)的实例
g_content_runner,初始化函数
Initialize(...)参数正是前面创建的
params。
运行
g_content_runner(ContentMainRunnerImpl)。
Created with Raphaël 2.1.0初始化g_content_main_delegate创建并初始化g_content_runner运行g_content_runner结束
在上面的过程中,
Class ContentMainRunnerImpl的
Initialize(...)里面有很多重要的步骤:
delegate_->BasicStartupComplete(…) 。
这里的delegate_就是
g_content_main_delegate。
BasicStartupComplete(…)中会实例化
ShellContentClient,并通过
Class ContentClient的
SetContentClient(…)设置全局变量
g_client(ShellContentClient)。
ContentClientInitializer::Set(process_type, delegate_)。
Class ContentClientInitializer位于
content_main_runner.cc中,其
Set(…)函数分别调用
g_content_main_delegate的
CreateContentBrowserClient(),
CreateContentGpuClient(),
CreateContentRendererClient()和
CreateContentUtilityClient(),并分别初始化
content_client(其实就是前面的全局变量
g_client)的变量
browser_(ShellContentBrowserClient),
gpu_(ContentGpuClient),
renderer_(ShellContentRendererClient)和
utility_(ShellContentUtilityClient)。
注意这里的前提条件
!define(CHROME_MULTIPLE_DLL_CHILD)和
!define(CHROME_MULTIPLE_DLL_BROWSER),这两个条件基于是否定义
is_multi_dll_chrome,而从
build/config/chrome_build.gni可以看到只有Windows平台上,才会定义。所以前提条件是满足的。
运行
g_content_runner执行的是
ContentMainRunnerImpl的函数
Run(),它会调用
RunNamedProcessTypeMain(…)来根据参数
process_type启动不同的进程,默认是空,启动的正式Browser进程。
在进程启动之前,会先调用
ShellMainDelegate::RunProcess(…),它会创建
BrowserMainRunner,并作为参数进一步调用
shell_browser_main.cc的
ShellBrowserMain(…),进而调用
BrowserMainRunner的
Initialize(…)。
在这个
Initialize(…)函数中,又会创建
BrowserMainLoop的实例,并对其依次进行如下调用:
Created with Raphaël 2.1.0Initialize(…)Init()EarlyInitialization()PreMainMessageLoopStart()PostMainMessageLoopStart()CreateStartupTasks()others
其中,
Init()执行
parts_.reset(GetContentClient()->browser()->CreateBrowserMainParts(parameters_)),
GetContentClient()获得的就是前面提到的
g_client,所以
GetContentClient()->browser()得到的就是
browser_(ShellContentBrowserClient),调用其
CreateBrowserMainParts(…)创建
ShellBrowserMainParts实例。
其他的函数调用,其实都是调用
ShellBrowserMainParts的函数,且都与GPU进程的启动相关,所以我们在后续文章继续分析。
回到
ContentMainRunnerImpl的函数
RunNamedProcessTypeMain(...),最后会调用
browser_main.cc的
BrowserMain(),进而调用
BrowserMainRunnerImpl的
Run()等,启动Browser进程。
综上,我们可以画出UML图便于理解,如下:
相关文章推荐
- Chromium的Plugin进程启动过程分析
- Android的启动过程分析(从进程和Framework的角度)-android学习之旅(98)
- Android的启动过程分析(从进程和Framework的角度)-android学习之旅(98)
- Android的启动过程分析(从进程和Framework的角度)-android学习之旅(98)
- Chromium的Render进程启动过程分析
- Android的启动过程分析(从进程和Framework的角度)-android学习之旅(98)
- Android的启动过程分析(从进程和Framework的角度)-android学习之旅(98)
- chromium for android Browser进程创建过程分析
- Chromium的GPU进程启动过程分析
- nginx源码分析(11)-进程启动分析(1)
- 分析Android 根文件系统启动过程(init守护进程分析)
- 分析Android 根文件系统启动过程(init守护进程分析)
- 分析Android 根文件系统启动过程(init守护进程分析)
- 分析Android 根文件系统启动过程(init守护进程分析)
- Linux启动 (4)-cpu_idle()进程后运行分析
- 分析Android 根文件系统启动过程(init守护进程分析)
- 分析Android 根文件系统启动过程(init守护进程分析)
- CEGUI 学习笔记 - 启动详细分析
- MTD系列 - android平台上linux启动时init进程解析init.rc文件分析
- [zz] 分析Android 根文件系统启动过程(init守护进程分析)