最近在研读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行:

  1. // Main routine for running as the Browser process.
  2. int BrowserMain(const MainFunctionParams& parameters) {
  3. ScopedBrowserMainEvent scoped_browser_main_event;
  4.  
  5. base::trace_event::TraceLog::GetInstance()->SetProcessName("Browser");
  6. base::trace_event::TraceLog::GetInstance()->SetProcessSortIndex(
  7. kTraceEventBrowserProcessSortIndex);
  8.  
  9. std::unique_ptr<BrowserMainRunner> main_runner(BrowserMainRunner::Create());
  10.  
  11. int exit_code = main_runner->Initialize(parameters);
  12. if (exit_code >= )
  13. return exit_code;
  14.  
  15. exit_code = main_runner->Run();
  16.  
  17. main_runner->Shutdown();
  18.  
  19. return exit_code;

当然,启动browser进程可以有多种方式,比如,shell模式的browser就是以ContentMain来启动的。这个函数很简单,创建了一个BrowserMainRunner对象,并且Run()起来就完成了所有的工作。

那么,事情肯定没这么简单,BrowserMainRunner就是browser进程执行主体实现,包揽了所有的事情,那么接着来看 在 chromium//src/content/browser/browser_main_runner.cc 239行

  1. // static
  2. BrowserMainRunner* BrowserMainRunner::Create() {
  3. return new BrowserMainRunnerImpl();
  4. }

原来BrowserMainRunner只是定义了接口,是由它的子类BrowserMainRunnerImpl来实现,通过Impl模式来隐藏细节,那么看main_runner的initialize和Run函数是如何实现的

chromium//src/content/browser/browser_main_runner.cc 51行

  1. class BrowserMainRunnerImpl : public BrowserMainRunner {
  2.  
  3. int Initialize(const MainFunctionParams& parameters) override {
  4. ......
  5. const base::TimeTicks start_time_step1 = base::TimeTicks::Now();
  6.  
  7. SkGraphics::Init();
  8.  
  9. base::StatisticsRecorder::Initialize();
  10.  
  11. notification_service_.reset(new NotificationServiceImpl);
  12. main_loop_.reset(new BrowserMainLoop(parameters));
  13.  
  14. main_loop_->Init();
  15.  
  16. main_loop_->EarlyInitialization();
  17. ......
  18.  
  19. main_loop_->PreMainMessageLoopStart();
  20. main_loop_->MainMessageLoopStart();
  21. main_loop_->PostMainMessageLoopStart();
  22. ui::InitializeInputMethod();
  23.  
  24. const base::TimeTicks start_time_step2 = base::TimeTicks::Now();
  25. main_loop_->CreateStartupTasks();
  26. int result_code = main_loop_->GetResultCode();
  27. if (result_code > )
  28. return result_code;
  29. .....
  30. }
  31.  
  32. int Run() override {
  33. DCHECK(initialization_started_);
  34. DCHECK(!is_shutdown_);
  35. main_loop_->RunMainMessageLoopParts();
  36. return main_loop_->GetResultCode();
  37. }
  38. }

可以看到 main_loop_ 成员接管了所有的工作,那么它的声明是什么:

  1. std::unique_ptr<BrowserMainLoop> main_loop_;

chromium//src/content/browser/browser_main_loop.h 里给出了 BrowserMainLoop的声明,BrowserMainLoop的初始化顺序如下:

  1. // Quick reference for initialization order:
  2. // Constructor
  3. // Init()
  4. // EarlyInitialization()
  5. // InitializeToolkit()
  6. // PreMainMessageLoopStart()
  7. // MainMessageLoopStart()
  8. // InitializeMainThread()
  9. // PostMainMessageLoopStart()
  10. // CreateStartupTasks()
  11. // PreCreateThreads()
  12. // CreateThreads()
  13. // BrowserThreadsStarted()
  14. // InitializeMojo()
  15. // InitStartupTracingForDuration()
  16. // PreMainMessageLoopRun()

这个流程已经在前面的BrowserMainRunnerImpl的实例对象的initialize已经完成,包含了大量信息,这里我就主要看主线程的IPC消息循环的部分,如下声明了IPC消息轮询对象:

  1. // Members initialized in |MainMessageLoopStart()|
  2. std::unique_ptr<base::MessageLoop> main_message_loop_;

chromium//src/content/browser/browser_main_loop.cc 710行,MainMessageLoopStart函数负责初始化成员变量main_message_loop_,如果当前进程没有就指向一个base::MessageLoopForUI指针.

  1. void BrowserMainLoop::MainMessageLoopStart() {
  2. // DO NOT add more code here. Use PreMainMessageLoopStart() above or
  3. // PostMainMessageLoopStart() below.
  4.  
  5. TRACE_EVENT0("startup", "BrowserMainLoop::MainMessageLoopStart");
  6.  
  7. // Create a MessageLoop if one does not already exist for the current thread.
  8. if (!base::MessageLoop::current())
  9. main_message_loop_.reset(new base::MessageLoopForUI);
  10.  
  11. InitializeMainThread();
  12. }

到了这里我们可以看到,base::MessageLoopForUI,顾名思义,是UI类型的IPC消息轮询,嗯,没错,Browser进程负责UI方面的IPC消息接收和转发(routing)。

接下来,就将这个消息轮询对象加入到主线程当中:

  1. void BrowserMainLoop::InitializeMainThread() {
  2. TRACE_EVENT0("startup", "BrowserMainLoop::InitializeMainThread");
  3. base::PlatformThread::SetName("CrBrowserMain");
  4.  
  5. // Register the main thread by instantiating it, but don't call any methods.
  6. main_thread_.reset(
  7. new BrowserThreadImpl(BrowserThread::UI, base::MessageLoop::current()));
  8. }

初始化完成之后,我们返回之前main_runner_的Run()是执行RunMainMessageLoopParts()函数:

  1. void BrowserMainLoop::RunMainMessageLoopParts() {
  2. // Don't use the TRACE_EVENT0 macro because the tracing infrastructure doesn't
  3. // expect synchronous events around the main loop of a thread.
  4. TRACE_EVENT_ASYNC_BEGIN0("toplevel", "BrowserMain:MESSAGE_LOOP", this);
  5.  
  6. bool ran_main_loop = false;
  7. if (parts_)
  8. ran_main_loop = parts_->MainMessageLoopRun(&result_code_);
  9.  
  10. if (!ran_main_loop)
  11. MainMessageLoopRun();
  12.  
  13. TRACE_EVENT_ASYNC_END0("toplevel", "BrowserMain:MESSAGE_LOOP", this);
  14. }

这里我们需要分析一下 BrowserMainParts 这个类以及它的子类,因为它开始涉及到平台相关的内容了。

BrowserMainParts的子类有ChromeBrowserMainParts, ChromeBrowserMainPartsAndroid,ChromeBrowserMainPartsPosix 等等,涉及chrome相关的,都没有重载MainMessageLoopRun,当然也有重载这个函数,比如ShellBrowserMainParts类。

那么在chromium//src/content/browser/browser_main_loop.cc 1708行

  1. void BrowserMainLoop::MainMessageLoopRun() {
  2. #if defined(OS_ANDROID)
  3. // Android's main message loop is the Java message loop.
  4. NOTREACHED();
  5. #else
  6. DCHECK(base::MessageLoopForUI::IsCurrent());
  7. if (parameters_.ui_task) {
  8. base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
  9. *parameters_.ui_task);
  10. }
  11.  
  12. base::RunLoop run_loop;
  13. run_loop.Run();
  14. #endif
  15. }

RunLoop是一个可以处理嵌套IPC消息的辅助类,它里面声明了一个 RunLoop::Delegate类,来协助完成对IPC消息轮询嵌套层级的执行。

这里简单解释一下消息嵌套,即当前处理的IPC消息过程中有收到了新的IPC消息。RunLoop以一种类似入栈出栈的思路来实现消息嵌套。

我们接着看RunLoop的声明:

  1. enum class Type {
  2. kDefault,
  3. kNestableTasksAllowed,
  4. };
  5.  
  6. RunLoop(Type type = Type::kDefault);

构造函数:

  1. RunLoop::RunLoop(Type type)
  2. : delegate_(tls_delegate.Get().Get()),
  3. type_(type),
  4. origin_task_runner_(ThreadTaskRunnerHandle::Get()),
  5. weak_factory_(this) {
  6. DCHECK(delegate_) << "A RunLoop::Delegate must be bound to this thread prior "
  7. "to using RunLoop.";
  8. DCHECK(origin_task_runner_);
  9.  
  10. DCHECK(IsNestingAllowedOnCurrentThread() ||
  11. type_ != Type::kNestableTasksAllowed);
  12. }

默认的话是不支持嵌套的,通过ThreadTaskRunnerHandle::Get()获取当前线程的IPC消息sender,并且偷偷的将从当前线程变量里的tls_delegate来初始化 RunLoop::Delegate delegate_ 成员变量,那么tls_delegate是什么呢?

在 chromium//src/base/run_loop.cc 73行

  1. // static
  2. RunLoop::Delegate::Client* RunLoop::RegisterDelegateForCurrentThread(
  3. Delegate* delegate) {
  4. // Bind |delegate| to this thread.
  5. DCHECK(!delegate->bound_);
  6. DCHECK_CALLED_ON_VALID_THREAD(delegate->bound_thread_checker_);
  7.  
  8. // There can only be one RunLoop::Delegate per thread.
  9. DCHECK(!tls_delegate.Get().Get());
  10. tls_delegate.Get().Set(delegate);
  11. delegate->bound_ = true;
  12.  
  13. return &delegate->client_interface_;
  14. }
  1. 函数RegisterDelegateForCurrentThread负责对tls_delegate进行赋值。而这个函数是在MessageLoopBindToCurrentThread函数里调用的。
  1. void MessageLoop::BindToCurrentThread() {
  2. DCHECK(!pump_);
  3. if (!pump_factory_.is_null())
  4. pump_ = std::move(pump_factory_).Run();
  5. else
  6. pump_ = CreateMessagePumpForType(type_);
  7.  
  8. DCHECK(!current()) << "should only have one message loop per thread";
  9. GetTLSMessageLoop()->Set(this);
  10.  
  11. incoming_task_queue_->StartScheduling();
  12. unbound_task_runner_->BindToCurrentThread();
  13. unbound_task_runner_ = nullptr;
  14. SetThreadTaskRunnerHandle();
  15. thread_id_ = PlatformThread::CurrentId();
  16.  
  17. scoped_set_sequence_local_storage_map_for_current_thread_ = std::make_unique<
  18. internal::ScopedSetSequenceLocalStorageMapForCurrentThread>(
  19. &sequence_local_storage_map_);
  20.  
  21. run_loop_client_ = RunLoop::RegisterDelegateForCurrentThread(this);
  22. }
  1.  

到这里,又悄悄的把MessageLoop和RunLoop联系到了一起,RunLoop的delegate_指向一个MessageLoop指针,那么我们接下来可以看一下MessageLoop的声明了。

  1. class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
  2. public RunLoop::Delegate {
  3. ......
  4.  
  5. }

好了,可以看到RunLoop的Run函数实际上是调用的MessageLoop的Run函数,来到了消息循环的主体,到此由于篇幅过长,那么下面单独写一章来看消息处理的具体流程。

  1.  

chromium源码阅读--Browser进程初始化的更多相关文章

  1. [原创]chromium源码阅读-进程间通信IPC.消息的接收与应答

    chromium源码阅读-进程间通信IPC.消息的接收与应答   chromium源码阅读-进程间通信IPC.消息的接收与应答 介绍 chromium进程间通信在win32下是通过命名管道的方式实现的 ...

  2. chromium源码阅读--进程间通信(IPC)

    第一篇就有提到Chromium是目前默认是采用多进程架构,当然,chromium有singe-process的版本. 多进程与多线程的区别,确实有很多可以讲的,我的另一篇博客也讲了一些,这里是从浏览器 ...

  3. chromium源码阅读--进程的Message Loop

    上一篇总结了chromium进程的启动,接下来就看线程的消息处理,这里的线程包含进程的主进程. 消息处理是由base::MessageLoop中实现,消息中的任务和定时器都是异步事件的. 主要如下几点 ...

  4. chromium源码阅读--HTTP Cache

    最近积累了一些关于HTTP缓存的知识,因此结合Chromium的实现总结一下,主要从如下2个分面: 1.HTTP缓存的基础知识 2.Chromium关于HTTP缓存的实现分析 一.HTTP缓存的基础知 ...

  5. chromium源码阅读

    linux下chromium的入口函数在文件:src/chrome/app/chrome_exe_main_aura.cc 中 int main(int argc, const char** argv ...

  6. chromium源码阅读--V8 Embbeding

    V8是google提供高性能JavaScript解释器,嵌入在chromium里执行JavaScript代码. V8本身是C++实现的,所有嵌入本身毫无压力,一起编译即可,不过作为一个动态语言解释器, ...

  7. chromium源码阅读--图片处理

    JavaScript 图像替换 JavaScript 图像替换技术检查设备能力,然后“做正确的事”. 您可以通过 window.devicePixelRatio 确定设备像素比,获取屏幕的宽度和高度, ...

  8. 初始化IoC容器(Spring源码阅读)

    初始化IoC容器(Spring源码阅读) 我们到底能走多远系列(31) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? ...

  9. Linux 源码阅读 进程管理

    Linux 源码阅读 进程管理 版本:2.6.24 1.准备知识 1.1 Linux系统中,进程是最小的调度单位: 1.2 PCB数据结构:task_struct (Location:linux-2. ...

随机推荐

  1. Sublime使用Ctrl+`作为快捷键弹出Console没有反映的解决办法

    很多Sublime新人都遇到了这个问题,到网上搜,信息很片面,而且不少都是旧版本的.于是有了这篇文章.       默认Sublime使用Ctrl+`作为快捷键弹出Console,但不同的系统抑或安装 ...

  2. 201521123049 《JAVA程序设计》 第12周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 将Student对象(属性:int id, String name,int age,doubl ...

  3. Linux-exec命令试验驱动(12)

    对于做驱动经常会使用exec来试验驱动,通过exec将-sh进程下的描述符指向我们的驱动,来实现调试 -sh进程常用描述符号: 0:标准输入 1:标准输出 2:错误信息 5:中断服务 exec命令使用 ...

  4. Hibernate第五篇【inverse、cascade属性详解】

    前言 上一篇博文已经讲解了一对多和多对一之间的关系了,一对多和多对一存在着关联关系(外键与主键的关系).本博文主要讲解Inverse属性.cascade属性.这两个属性对关联关系都有影响 Invers ...

  5. 02函数-05-generator(ES6)

    generator(生成器)是ES6标准引入的新的数据类型. generator看上去像一个函数,但可以返回多次,除了return语句,还可以用yield返回多次.定义方式如下: function* ...

  6. 鸟哥Linux学习笔记05

    1,          文件系统通常会将 权限与属性放置到inode中,至于实际数据则放置到data block块中.另外还有一个超级块(superblock)会记录整个文件系统的整体内容,包括ino ...

  7. JVM菜鸟进阶高手之路十(基础知识开场白)

    转载请注明原创出处,谢谢! 最近没有什么实战,准备把JVM知识梳理一遍,先以开发人员的交流来谈谈jvm这块的知识以及重要性,依稀记得2.3年前用solr的时候老是经常oom,提到oom大家应该都不陌生 ...

  8. postman安装使用教程---图文讲解

    一.安装postman 1,安装包安装 官网下载地址:https://www.getpostman.com 选择好对应的版本下载,下载完后直接安装 2,插件包安装 可以在谷歌的应用商店里面找到,或者在 ...

  9. 【编程之外】还记得曾经给'大学导师'写过的报告嘛 --> 前方高能

    写在前面 本文不是讲技术的,也没什么代码可看 本文不是讲技术的,也没什么代码可看 本文不是讲技术的,也没什么代码可看 还记得我们曾经给我们大学''导师''写过的报告嘛? 大学他愿意在凌晨6点向你询问近 ...

  10. 【Conclusion】MySQL使用

    MySQL使用 因为数据库实验用到了MySQL,这里对现在已经涉及到的MySQL部分操作做一个简单的小结. 1.安装MySQL 上MySQL的官网下载对应自己OS平台的MySQL安装文件,有在线安装和 ...