第一篇就有提到Chromium是目前默认是采用多进程架构,当然,chromium有singe-process的版本。

多进程与多线程的区别,确实有很多可以讲的,我的另一篇博客也讲了一些 (Linux 进程,线程),这里是从浏览器的角度来说,如果是多线程,如果一个线程崩溃,影响了整个浏览器的使用,因为在现在的网页标准更新了很多个版本,会有不同标准的页面在网络上,极大可能出现解析,渲染,插件等问题,那么对于用户来说,体验就会差很多了,浏览一个页面出问题,就要重启浏览器。而多进程则可以避免此问题,render进程崩溃只会影响当前的tab。

嗯,上面说了那么多,就是为了说,多进程之间就需要进程通信来协作,而chromium的进程间通信是非常繁杂的,如何处理这个是我们需要了解的关键。

那么本质的问题就是:

1、发那些消息(Message Type)

2、消息通道是怎么建立的 (Message Channel)

3、发送者和接收者(Sender,Listener)

OK,咱一个个来。

一、 Message Type

主要分为2类:“routed” 和 “control”。

1、routed消息

主要是用来给某个RenderViewHost对象发送消息的。不过,任何类都可以通过GetNextRoutingID 和 AddRoute 注册,就能接收routed消息。

2、control消息

control消息有创建pipe的类处理,当然这些类也可以接收routed消息。比如,请求资源或修改剪贴板不是特定于视图的,所以是控制消息。

3、消息的声明

 IPC_MESSAGE_ROUTED2(FrameHostMsg_MyMessage, GURL, int)

这个宏用来声明routed消息,这里声明了一个从render进程发送到browser进程的消息,并有一个GURL参数,一个int参数

 IPC_MESSAGE_CONTROL0(FrameMsg_MyMessage)

这个宏用来声明control消息,这里声明了一个从browser进程发送到render进程的消息,没有参数。

这里还有几个默认的约定:

(1)这些宏后面的数字表明有几个参数,最多5个参数,即: IPC_MESSAGE_ROUTED0~IPC_MESSAGE_ROUTED5 或者 IPC_MESSAGE_CONTROL0~IPC_MESSAGE_CONTROL5

(2)消息名称表明消息的接受者,FrameHostMsg,带Host后缀的类,表示在browser进程接收处理的消息,FrameMsg,则表示在render进程处理的消息,如果是Plugin进程,也会带有Plugin字样。

二、Message Channel

chromium的使用mojo IPC,并且在官网提供了性能对比 (Times in microseconds)

 

Windows Z840

Linux Z620

MacBook Pro 15" 2016

IPC

36.9

69.5

52.5

Mojo cross-process

28.2

48

34.9

这里是官网关于mojo的一些介绍,https://chromium.googlesource.com/chromium/src/+/master/mojo/README.md#System-Overview

从unittest看channel的创建:

 void IPCChannelMojoTestBase::CreateChannel(IPC::Listener* listener) {
channel_ =
IPC::ChannelMojo::Create(TakeHandle(), IPC::Channel::MODE_SERVER,
listener, base::ThreadTaskRunnerHandle::Get());
}

在IPC::ChannelMojo::Create里看到需要 IPC::ChannelMojo的构造,

 ChannelMojo::ChannelMojo(
mojo::ScopedMessagePipeHandle handle,
Mode mode,
Listener* listener,
const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner)
: task_runner_(ipc_task_runner),
pipe_(handle.get()),
listener_(listener),
weak_factory_(this) {
weak_ptr_ = weak_factory_.GetWeakPtr();
bootstrap_ = MojoBootstrap::Create(std::move(handle), mode, ipc_task_runner);
}

在MojoBootstrapImpl里完成sender和listener的绑定:

 class MojoBootstrapImpl : public MojoBootstrap {
public:
MojoBootstrapImpl(
mojo::ScopedMessagePipeHandle handle,
const scoped_refptr<ChannelAssociatedGroupController> controller)
: controller_(controller),
associated_group_(controller),
handle_(std::move(handle)) {} ~MojoBootstrapImpl() override {
controller_->ShutDown();
} private:
void Connect(mojom::ChannelAssociatedPtr* sender,
mojom::ChannelAssociatedRequest* receiver) override {
controller_->Bind(std::move(handle_));
controller_->CreateChannelEndpoints(sender, receiver);
} 。。。
}

上面的mojo  Channel的创建过程,linux提供的IPC比如:pipe,unix socket,share memory都不是线程安全的,mojo封装了底层IPC细节并提供了线程安全保障,并且看上面的性能对比,mojo性能更好,这也是chromium逐渐转用mojo的主要因素吧。

OK,上面介绍了mojo,接下来我们会发现,在进程里都是使用IPC::ChannelProxy这个类来代理完成Channel的各种工作。

这里我们只需看一个例子就能理解了,比如在browser进程的RenderProcessHost类里声明了GetChannel接口:

 IPC::ChannelProxy* GetChannel() = ;

根据chromium的套路,你大致就能想到,有一个RenderProcessHostImpl类会来实现这个接口,嗯,果不其然:

 class CONTENT_EXPORT RenderProcessHostImpl
: public RenderProcessHost,
public ChildProcessLauncher::Client,
public ui::GpuSwitchingObserver,
public mojom::RouteProvider,
public mojom::AssociatedInterfaceProvider,
public mojom::RendererHost {
...
IPC::ChannelProxy* GetChannel() override;
...
}

我们可以看到这里会提供一个IPC::ChannelProxy的指针,那么顺着这个,ChannelProxy的创建和初始化就不远了。

bool RenderProcessHostImpl::Init() {
...
if (!channel_)
InitializeChannelProxy(); ...
CreateMessageFilters();
RegisterMojoInterfaces();
... }

可以看到,上面初始化了Channel并给当前实例创建了MessageFilter和在mojo里注册了消息发送的mojo interface。

mojo会负责将channel两端连通,之后的消息发送就可使用IPC::ChannelProxy来完成了。

三、发送者和接收者

1、发送者

chromium里定义了IPC::Sender的接口:

 class Message;

 class IPC_EXPORT Sender {
public:
// Sends the given IPC message. The implementor takes ownership of the
// given Message regardless of whether or not this method succeeds. This
// is done to make this method easier to use. Returns true on success and
// false otherwise.
virtual bool Send(Message* msg) = ; protected:
virtual ~Sender() {}
};

上面的使用例子,我们可以看到 IPC::ChannelProxy 是消息的发送者,看类的声明:

 class IPC_EXPORT ChannelProxy : public Sender {

 }

2、接收者

同样chromium也定义Listener。

class Message;

// Implemented by consumers of a Channel to receive messages.
class IPC_EXPORT Listener {
public:
// Called when a message is received. Returns true iff the message was
// handled.
virtual bool OnMessageReceived(const Message& message) = ; ...
};

我们在前面提到的router,是消息接收者,也是消息发送者:

 class IPC_EXPORT MessageRouter : public Listener, public Sender {
...
}

还有子线程实例也是Listener:

 class CONTENT_EXPORT ChildThreadImpl
: public IPC::Listener,
virtual public ChildThread,
private base::FieldTrialList::Observer,
public mojom::RouteProvider,
public mojom::AssociatedInterfaceProvider,
public mojom::ChildControl {
...
}

好了,更多例子我也不举了,chromium IPC还有更多的内容,在代码待我们学习,这里暂时总结到这里,后续再补充。

chromium源码阅读--进程间通信(IPC)的更多相关文章

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

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

  2. chromium源码阅读--Browser进程初始化

    最近在研读chromium源码,经过一段懵懂期,查阅了官网和网上的技术文章,是时候自己总结一下了,首先IPC message loop开始吧,这是每个主线程必须有的一个IPC消息轮训主体,类似之前的q ...

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

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

  4. chromium源码阅读--HTTP Cache

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

  5. chromium源码阅读--V8 Embbeding

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

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

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

  7. chromium源码阅读

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

  8. Android源码阅读 – Zygote

    @Dlive 本文档: 使用的Android源码版本为:Android-4.4.3_r1 kitkat (源码下载: http://source.android.com/source/index.ht ...

  9. Chromium源码--网络请求流程分析

    转载请注明出处:http://www.cnblogs.com/fangkm/p/3784660.html 本文探讨一下chromium中加载URL的流程,具体来说是从地址栏输入URL地址到通过URLR ...

随机推荐

  1. OSGi-开发环境的建立和HelloWorld(04)

    1 OSGi开发环境的建立 1.1 Equinox是什么 从代码角度来看,Equinox其实就是OSGi核心标准的完整实现,并且还在这个基础上增加了一些额外的功能(比如为框架增加了命令行和程序执行的入 ...

  2. JavaScript基礎知識

    JavaScript基礎知識 1.標籤組使用 <script charset='utf-8' //設置字元集 defet //使腳本延遲到文檔解析完成,Browser已忽略 language=' ...

  3. devstack安装openstack newton版本

    准备使用devstack安装openstack N版,搞一套开发环境出来.一连整了4天,遇到各种问题,各种错误,一直到第4天下午4点多才算完成. 在这个过程中感觉到使用devstack搭建openst ...

  4. mybatis运行时错误Illegal argument exception argument type mismatch

    使用注解时遇到该错误 使用XML应该也会有相应的错误 解决办法:查看是不是Dao接口的参数列表没有加@Param注解  参数过多时需要该注解指明参数

  5. bzoj2257 [Jsoi2009]瓶子和燃料 最大公约数

    [Jsoi2009]瓶子和燃料 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1449  Solved: 889[Submit][Status][Di ...

  6. bzoj4198 荷马史诗 哈夫曼编码

    逐影子的人,自己就是影子. --荷马 Allison 最近迷上了文学.她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的<荷马史诗>.但是由<奥德赛>和&l ...

  7. 箱线图(boxplot)简介与举例

    简述:   盒图是在1977年由美国的统计学家约翰·图基(John Tukey)发明的.它由五个数值点组成:最小值(min),下四分位数(Q1),中位数(median),上四分位数(Q3),最大值(m ...

  8. CSS中新属性calc()

    CSS3的calc()使用 原文: http://www.w3cplus.com/css3/how-to-use-css3-calc-function.html © w3cplus.com calc( ...

  9. ch6-条件渲染(v-if v-else v-else-if key管理可复用元素 v-show )

    1 v-if 1.1 简单使用 <h1 class="h1" v-if="ok">yes</h1> <script> var ...

  10. C#中 什么是装箱和拆箱

    装箱:将值类型包装为引用类型 拆箱:将引用类型转换为值类型   例如 objetct obj = null; obj = ; //装箱 int i = (int) obj; //拆箱