第一篇就有提到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. 嵌入系统squashfs挂载常见问题总结

    由于squahsfs的一些优点,嵌入系统常常直接使用squashfs作为initrd挂载到/dev/ram,作为rootfs.这里对常见的一些问题进行一些分析. 1. kernel启动出现错误 RAM ...

  2. 记录兼容IE8中发现的一些问题

    1.new Date().getYear(); chrome下:获取的是1900年之后的年份,如2017年获取的是117 IE8下:获取的是公元年份,如2017获取的是2017 解决方案:使用new ...

  3. 基于pytorch实现HighWay Networks之Train Deep Networks

    (一)Highway Networks 与 Deep Networks 的关系 理论实践表明神经网络的深度是至关重要的,深层神经网络在很多方面都已经取得了很好的效果,例如,在1000-class Im ...

  4. String.getBytes(),源码之下,了无秘密

    @Deprecated public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) { if (srcBegin ...

  5. Python uwsgi 无法安装以及编译报错的处理方式

    之前安装uwsgi的时候编译一步有出错,因为比较早,部分错误代码已经找不到了,网上找了部分错误信息, 现把解决方式共享出来. 环境:CentOS release 6.4   Python 2.7.3 ...

  6. HDFS源码分析之NameNode(2)————Format

    在Hadoop的HDFS部署好了之后并不能马上使用,而是先要对配置的文件系统进行格式化.在这里要注意两个概念,一个是文件系统,此时的文件系统在物理上还不存在,或许是网络磁盘来描述会更加合适:二就是格式 ...

  7. Linux入门之常用命令(10)软连接 硬链接

    在Linux系统中,内核为每一个新创建的文件分配一个Inode(索引结点),每个文件都有一个惟一的inode号.文件属性保存在索引结点里,在访问文件时,索引结点被复制到内存在,从而实现文件的快速访问. ...

  8. Beauty Contest 凸包+旋转卡壳法

    Beauty Contest Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 27507   Accepted: 8493 D ...

  9. E. Fish (概率DP)

    E. Fish time limit per test 3 seconds memory limit per test 128 megabytes input standard input outpu ...

  10. Football 概率DP poj3071

                                                                                                 Footbal ...