一起学习Chromium之Browser进程启动分析

来源:互联网 发布:艾吉森空气净化器 知乎 编辑:程序博客网 时间:2024/06/08 00:36

前言

从接触Chromium开始,参考过很多网上的分析文章,获益匪浅。本系列文章尝试着从新的角度,逐步分析Chromium的部分关键知识点,希望能分析清楚初学者常常比较困惑的问题,比如Chromium怎么启动,各个进程(Browser/Render/GPU等)怎么启动,Chromium怎么显示一个网页等,进而理顺Chromium中各种类的关系和作用。而作者也会在文章中提出自己尚未理解的问题,希望能达到互动研究共同提高的效果。


ContentShell

鉴于Chromium本身的复杂和庞大,从ContentShell来分析是一个比较好的选择。关于Content模块和Chromium的关系,可以参考这里。

ContentShell App的启动是从ContentShellActivity.javaonCreate(...)开始,这里首先会初始化CommandLine,利用CommandLine可以设置各种启动参数,比如singleprocess,GPU hardware acceleration等,这里详细列出了Chrome中支持的command lines。但这部分和本文主题无关,暂且略过。

接着会调用LibraryLoader.java的静态函数get(...)来实例化LibraryLoader,并指定LibraryProcessTypePROCESS_BROWSER。从名字可以看出来,LibraryLoader的目的就是加载和注册native的libraries。这是通过ensureInitialized(...)来调用loadAlreadyLocked(...)initializeAlreadyLocked(...)来完成的。在loadAlreadyLocked(...)中,通过Linker.loadLibrary(...)或者System.loadLibrary(...)来触发native层的JNI_OnLoad(...)。ContentShell对应的native文件是shell_library_loader.cc,可以看到在JNI_OnLoad(...)中主要做了两件事情:

  1. Compositor的初始化。
  2. 创建并设置ContentMainDelegate,这里是ShellMainDelegate.

Compositor的初始化是在content/browser/renderer_host/compositor_impl_android.ccInitialize()完成的,但其实只是设置了变量g_initialized意义是什么?)。而ShellMainDelegate是比较核心的class,后面的分析会多次提到。

现在回到ContentShellActivity.java,会看到接下来的重点在于BrowserStartupController。先是通过其静态函数get(...)来实例化一个class,并设置其LibraryProcessTypePROCESS_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 ContentMainRunnerImplInitialize(...)里面有很多重要的步骤:

  • delegate_->BasicStartupComplete(…)

    这里的delegate_就是g_content_main_delegateBasicStartupComplete(…)中会实例化ShellContentClient,并通过Class ContentClientSetContentClient(…)设置全局变量g_client(ShellContentClient)

  • ContentClientInitializer::Set(process_type, delegate_)

    Class ContentClientInitializer位于content_main_runner.cc中,其Set(…)函数分别调用g_content_main_delegateCreateContentBrowserClient()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.ccShellBrowserMain(…),进而调用BrowserMainRunnerInitialize(…)

    在这个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.ccBrowserMain(),进而调用BrowserMainRunnerImplRun()等,启动Browser进程。

综上,我们可以画出UML图便于理解,如下:

Browser进程启动关键类图

1 0
原创粉丝点击