chromium源码阅读--Browser进程初始化
最近在研读chromium源码,经过一段懵懂期,查阅了官网和网上的技术文章,是时候自己总结一下了,首先从Browser进程启动以及IPC message loop开始吧,这是每个主线程必须有的一个IPC消息轮训主体,类似之前的quagga里thread。
首先来看看chromium的多进程模型:

图1 多进程模型
图1描述了chromium里 browser进程,(隐含了zygote进程),render进程以及 WebKit的关系,Webkit是网页渲染引擎,这里我就不发散开了。
浏览器进程有 browser进程,render进程,还有GPU进程,plugin进程等等,首先启动肯定是browser进程。
那么我们先从browser进程开始, 以下是Browser进程主函数, 在chromium//src/content/browser/browser_main.cc 33行:
// Main routine for running as the Browser process.
int BrowserMain(const MainFunctionParams& parameters) {
ScopedBrowserMainEvent scoped_browser_main_event; base::trace_event::TraceLog::GetInstance()->SetProcessName("Browser");
base::trace_event::TraceLog::GetInstance()->SetProcessSortIndex(
kTraceEventBrowserProcessSortIndex); std::unique_ptr<BrowserMainRunner> main_runner(BrowserMainRunner::Create()); int exit_code = main_runner->Initialize(parameters);
if (exit_code >= )
return exit_code; exit_code = main_runner->Run(); main_runner->Shutdown(); return exit_code;
当然,启动browser进程可以有多种方式,比如,shell模式的browser就是以ContentMain来启动的。这个函数很简单,创建了一个BrowserMainRunner对象,并且Run()起来就完成了所有的工作。
那么,事情肯定没这么简单,BrowserMainRunner就是browser进程执行主体实现,包揽了所有的事情,那么接着来看 在 chromium//src/content/browser/browser_main_runner.cc 239行。
// static
BrowserMainRunner* BrowserMainRunner::Create() {
return new BrowserMainRunnerImpl();
}
原来BrowserMainRunner只是定义了接口,是由它的子类BrowserMainRunnerImpl来实现,通过Impl模式来隐藏细节,那么看main_runner的initialize和Run函数是如何实现的
在chromium//src/content/browser/browser_main_runner.cc 51行
class BrowserMainRunnerImpl : public BrowserMainRunner {
int Initialize(const MainFunctionParams& parameters) override {
......
const base::TimeTicks start_time_step1 = base::TimeTicks::Now();
SkGraphics::Init();
base::StatisticsRecorder::Initialize();
notification_service_.reset(new NotificationServiceImpl);
main_loop_.reset(new BrowserMainLoop(parameters));
main_loop_->Init();
main_loop_->EarlyInitialization();
......
main_loop_->PreMainMessageLoopStart();
main_loop_->MainMessageLoopStart();
main_loop_->PostMainMessageLoopStart();
ui::InitializeInputMethod();
const base::TimeTicks start_time_step2 = base::TimeTicks::Now();
main_loop_->CreateStartupTasks();
int result_code = main_loop_->GetResultCode();
if (result_code > )
return result_code;
.....
}
int Run() override {
DCHECK(initialization_started_);
DCHECK(!is_shutdown_);
main_loop_->RunMainMessageLoopParts();
return main_loop_->GetResultCode();
}
}
可以看到 main_loop_ 成员接管了所有的工作,那么它的声明是什么:
std::unique_ptr<BrowserMainLoop> main_loop_;
在 chromium//src/content/browser/browser_main_loop.h 里给出了 BrowserMainLoop的声明,BrowserMainLoop的初始化顺序如下:
// Quick reference for initialization order:
// Constructor
// Init()
// EarlyInitialization()
// InitializeToolkit()
// PreMainMessageLoopStart()
// MainMessageLoopStart()
// InitializeMainThread()
// PostMainMessageLoopStart()
// CreateStartupTasks()
// PreCreateThreads()
// CreateThreads()
// BrowserThreadsStarted()
// InitializeMojo()
// InitStartupTracingForDuration()
// PreMainMessageLoopRun()
这个流程已经在前面的BrowserMainRunnerImpl的实例对象的initialize已经完成,包含了大量信息,这里我就主要看主线程的IPC消息循环的部分,如下声明了IPC消息轮询对象:
// Members initialized in |MainMessageLoopStart()|
std::unique_ptr<base::MessageLoop> main_message_loop_;
在chromium//src/content/browser/browser_main_loop.cc 710行,MainMessageLoopStart函数负责初始化成员变量main_message_loop_,如果当前进程没有就指向一个base::MessageLoopForUI指针.
void BrowserMainLoop::MainMessageLoopStart() {
// DO NOT add more code here. Use PreMainMessageLoopStart() above or
// PostMainMessageLoopStart() below.
TRACE_EVENT0("startup", "BrowserMainLoop::MainMessageLoopStart");
// Create a MessageLoop if one does not already exist for the current thread.
if (!base::MessageLoop::current())
main_message_loop_.reset(new base::MessageLoopForUI);
InitializeMainThread();
}
到了这里我们可以看到,base::MessageLoopForUI,顾名思义,是UI类型的IPC消息轮询,嗯,没错,Browser进程负责UI方面的IPC消息接收和转发(routing)。
接下来,就将这个消息轮询对象加入到主线程当中:
void BrowserMainLoop::InitializeMainThread() {
TRACE_EVENT0("startup", "BrowserMainLoop::InitializeMainThread");
base::PlatformThread::SetName("CrBrowserMain");
// Register the main thread by instantiating it, but don't call any methods.
main_thread_.reset(
new BrowserThreadImpl(BrowserThread::UI, base::MessageLoop::current()));
}
初始化完成之后,我们返回之前main_runner_的Run()是执行RunMainMessageLoopParts()函数:
void BrowserMainLoop::RunMainMessageLoopParts() {
// Don't use the TRACE_EVENT0 macro because the tracing infrastructure doesn't
// expect synchronous events around the main loop of a thread.
TRACE_EVENT_ASYNC_BEGIN0("toplevel", "BrowserMain:MESSAGE_LOOP", this);
bool ran_main_loop = false;
if (parts_)
ran_main_loop = parts_->MainMessageLoopRun(&result_code_);
if (!ran_main_loop)
MainMessageLoopRun();
TRACE_EVENT_ASYNC_END0("toplevel", "BrowserMain:MESSAGE_LOOP", this);
}
这里我们需要分析一下 BrowserMainParts 这个类以及它的子类,因为它开始涉及到平台相关的内容了。
BrowserMainParts的子类有ChromeBrowserMainParts, ChromeBrowserMainPartsAndroid,ChromeBrowserMainPartsPosix 等等,涉及chrome相关的,都没有重载MainMessageLoopRun,当然也有重载这个函数,比如ShellBrowserMainParts类。
那么在chromium//src/content/browser/browser_main_loop.cc 1708行
void BrowserMainLoop::MainMessageLoopRun() {
#if defined(OS_ANDROID)
// Android's main message loop is the Java message loop.
NOTREACHED();
#else
DCHECK(base::MessageLoopForUI::IsCurrent());
if (parameters_.ui_task) {
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
*parameters_.ui_task);
}
base::RunLoop run_loop;
run_loop.Run();
#endif
}
RunLoop是一个可以处理嵌套IPC消息的辅助类,它里面声明了一个 RunLoop::Delegate类,来协助完成对IPC消息轮询嵌套层级的执行。
这里简单解释一下消息嵌套,即当前处理的IPC消息过程中有收到了新的IPC消息。RunLoop以一种类似入栈出栈的思路来实现消息嵌套。
我们接着看RunLoop的声明:
enum class Type {
kDefault,
kNestableTasksAllowed,
};
RunLoop(Type type = Type::kDefault);
构造函数:
RunLoop::RunLoop(Type type)
: delegate_(tls_delegate.Get().Get()),
type_(type),
origin_task_runner_(ThreadTaskRunnerHandle::Get()),
weak_factory_(this) {
DCHECK(delegate_) << "A RunLoop::Delegate must be bound to this thread prior "
"to using RunLoop.";
DCHECK(origin_task_runner_); DCHECK(IsNestingAllowedOnCurrentThread() ||
type_ != Type::kNestableTasksAllowed);
}
默认的话是不支持嵌套的,通过ThreadTaskRunnerHandle::Get()获取当前线程的IPC消息sender,并且偷偷的将从当前线程变量里的tls_delegate来初始化 RunLoop::Delegate delegate_ 成员变量,那么tls_delegate是什么呢?
在 chromium//src/base/run_loop.cc 73行
// static
RunLoop::Delegate::Client* RunLoop::RegisterDelegateForCurrentThread(
Delegate* delegate) {
// Bind |delegate| to this thread.
DCHECK(!delegate->bound_);
DCHECK_CALLED_ON_VALID_THREAD(delegate->bound_thread_checker_); // There can only be one RunLoop::Delegate per thread.
DCHECK(!tls_delegate.Get().Get());
tls_delegate.Get().Set(delegate);
delegate->bound_ = true; return &delegate->client_interface_;
}
函数RegisterDelegateForCurrentThread负责对tls_delegate进行赋值。而这个函数是在MessageLoop的BindToCurrentThread函数里调用的。
void MessageLoop::BindToCurrentThread() {
DCHECK(!pump_);
if (!pump_factory_.is_null())
pump_ = std::move(pump_factory_).Run();
else
pump_ = CreateMessagePumpForType(type_);
DCHECK(!current()) << "should only have one message loop per thread";
GetTLSMessageLoop()->Set(this);
incoming_task_queue_->StartScheduling();
unbound_task_runner_->BindToCurrentThread();
unbound_task_runner_ = nullptr;
SetThreadTaskRunnerHandle();
thread_id_ = PlatformThread::CurrentId();
scoped_set_sequence_local_storage_map_for_current_thread_ = std::make_unique<
internal::ScopedSetSequenceLocalStorageMapForCurrentThread>(
&sequence_local_storage_map_);
run_loop_client_ = RunLoop::RegisterDelegateForCurrentThread(this);
}
到这里,又悄悄的把MessageLoop和RunLoop联系到了一起,RunLoop的delegate_指向一个MessageLoop指针,那么我们接下来可以看一下MessageLoop的声明了。
class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
public RunLoop::Delegate {
...... }
好了,可以看到RunLoop的Run函数实际上是调用的MessageLoop的Run函数,来到了消息循环的主体,到此由于篇幅过长,那么下面单独写一章来看消息处理的具体流程。
chromium源码阅读--Browser进程初始化的更多相关文章
- [原创]chromium源码阅读-进程间通信IPC.消息的接收与应答
chromium源码阅读-进程间通信IPC.消息的接收与应答 chromium源码阅读-进程间通信IPC.消息的接收与应答 介绍 chromium进程间通信在win32下是通过命名管道的方式实现的 ...
- chromium源码阅读--进程间通信(IPC)
第一篇就有提到Chromium是目前默认是采用多进程架构,当然,chromium有singe-process的版本. 多进程与多线程的区别,确实有很多可以讲的,我的另一篇博客也讲了一些,这里是从浏览器 ...
- chromium源码阅读--进程的Message Loop
上一篇总结了chromium进程的启动,接下来就看线程的消息处理,这里的线程包含进程的主进程. 消息处理是由base::MessageLoop中实现,消息中的任务和定时器都是异步事件的. 主要如下几点 ...
- chromium源码阅读--HTTP Cache
最近积累了一些关于HTTP缓存的知识,因此结合Chromium的实现总结一下,主要从如下2个分面: 1.HTTP缓存的基础知识 2.Chromium关于HTTP缓存的实现分析 一.HTTP缓存的基础知 ...
- chromium源码阅读
linux下chromium的入口函数在文件:src/chrome/app/chrome_exe_main_aura.cc 中 int main(int argc, const char** argv ...
- chromium源码阅读--V8 Embbeding
V8是google提供高性能JavaScript解释器,嵌入在chromium里执行JavaScript代码. V8本身是C++实现的,所有嵌入本身毫无压力,一起编译即可,不过作为一个动态语言解释器, ...
- chromium源码阅读--图片处理
JavaScript 图像替换 JavaScript 图像替换技术检查设备能力,然后“做正确的事”. 您可以通过 window.devicePixelRatio 确定设备像素比,获取屏幕的宽度和高度, ...
- 初始化IoC容器(Spring源码阅读)
初始化IoC容器(Spring源码阅读) 我们到底能走多远系列(31) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? ...
- Linux 源码阅读 进程管理
Linux 源码阅读 进程管理 版本:2.6.24 1.准备知识 1.1 Linux系统中,进程是最小的调度单位: 1.2 PCB数据结构:task_struct (Location:linux-2. ...
随机推荐
- 201521123075 《Java程序设计》第8周学习总结
1. 本周学习总结 2. 书面作业 本次作业题集集合 1.List中指定元素的删除(题目4-1) 1.1 实验总结 进行删除操作的时候最好从末尾开始删除.如果从开头开始删除,会使每个元素的对应位置发生 ...
- 201521123030《Java程序设计》第6周学习总结
1. 本周学习总结 2. 书面作业 1.clone方法 1.1 Object对象中的clone方法是被protected修饰,在自定义的类中覆盖clone方法时需要注意什么? 覆盖clone的方法,声 ...
- 201521123080《Java程序设计》第1周学习总结
#1. 本周学习总结 Java开发环境,如何用记事本和eclipse进行编程. #2. 书面作业 Q1.为什么java程序可以跨平台运行?执行java程序的步骤是什么?(请用自己的语言书写) ...
- 201521123026《JAVA程序设计》第13周学习总结
1. 本周学习总结 以你喜欢的方式(思维导图.OneNote或其他)归纳总结多网络相关内容. 2. 书面作业 1. 网络基础 1.1 比较ping www.baidu.com与ping cec.jmu ...
- PHP连接数据_insert_id介绍
对于自增长的主键列不好取值的情况,php提供了一个变量来取值,insert_id $db = new MySQLi("localhost","root",&qu ...
- 参加IMWebConf 2017 前端开发者大会是什么体验?
周六作为特邀讲师之一参加了IMWebConf 2017 前端开发者大会的主题演讲,主题为<WebAssembly:面向未来的web开发技术>.本次大会质量非常高,来自国内外的技术专家带了很 ...
- webservice05#soap消息
1, SOAPMessage结构图 2, SOAP消息的创建 1>前面的一个简单WebService 服务 package com.yangw.soap.service; import jav ...
- Spring4 customEditors
Spring4.0版本以后customEditors属性为Map<Class<?>, Class<? extends PropertyEditor>>,所以用key ...
- 从Leetcode的Combination Sum系列谈起回溯法
在LeetCode上面有一组非常经典的题型--Combination Sum,从1到4.其实就是类似于给定一个数组和一个整数,然后求数组里面哪几个数的组合相加结果为给定的整数.在这个题型系列中,1.2 ...
- C#最基本的小说爬虫
新手学习C#,自己折腾弄了个简单的小说爬虫,实现了把小说内容爬下来写入txt,还只能爬指定网站. 第一次搞爬虫,涉及到了网络协议,正则表达式,弄得手忙脚乱跑起来效率还差劲,慢慢改吧. 爬的目标:htt ...