Chromium学习笔记:程序启动入口分析(Windows)
转自:Chromium学习笔记:程序启动入口分析(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 的交互。

浙公网安备 33010602011771号