chromium源码阅读--进程间通信(IPC)
第一篇就有提到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)的更多相关文章
- [原创]chromium源码阅读-进程间通信IPC.消息的接收与应答
chromium源码阅读-进程间通信IPC.消息的接收与应答 chromium源码阅读-进程间通信IPC.消息的接收与应答 介绍 chromium进程间通信在win32下是通过命名管道的方式实现的 ...
- chromium源码阅读--Browser进程初始化
最近在研读chromium源码,经过一段懵懂期,查阅了官网和网上的技术文章,是时候自己总结一下了,首先IPC message loop开始吧,这是每个主线程必须有的一个IPC消息轮训主体,类似之前的q ...
- chromium源码阅读--进程的Message Loop
上一篇总结了chromium进程的启动,接下来就看线程的消息处理,这里的线程包含进程的主进程. 消息处理是由base::MessageLoop中实现,消息中的任务和定时器都是异步事件的. 主要如下几点 ...
- chromium源码阅读--HTTP Cache
最近积累了一些关于HTTP缓存的知识,因此结合Chromium的实现总结一下,主要从如下2个分面: 1.HTTP缓存的基础知识 2.Chromium关于HTTP缓存的实现分析 一.HTTP缓存的基础知 ...
- chromium源码阅读--V8 Embbeding
V8是google提供高性能JavaScript解释器,嵌入在chromium里执行JavaScript代码. V8本身是C++实现的,所有嵌入本身毫无压力,一起编译即可,不过作为一个动态语言解释器, ...
- chromium源码阅读--图片处理
JavaScript 图像替换 JavaScript 图像替换技术检查设备能力,然后“做正确的事”. 您可以通过 window.devicePixelRatio 确定设备像素比,获取屏幕的宽度和高度, ...
- chromium源码阅读
linux下chromium的入口函数在文件:src/chrome/app/chrome_exe_main_aura.cc 中 int main(int argc, const char** argv ...
- Android源码阅读 – Zygote
@Dlive 本文档: 使用的Android源码版本为:Android-4.4.3_r1 kitkat (源码下载: http://source.android.com/source/index.ht ...
- Chromium源码--网络请求流程分析
转载请注明出处:http://www.cnblogs.com/fangkm/p/3784660.html 本文探讨一下chromium中加载URL的流程,具体来说是从地址栏输入URL地址到通过URLR ...
随机推荐
- OSGi-开发环境的建立和HelloWorld(04)
1 OSGi开发环境的建立 1.1 Equinox是什么 从代码角度来看,Equinox其实就是OSGi核心标准的完整实现,并且还在这个基础上增加了一些额外的功能(比如为框架增加了命令行和程序执行的入 ...
- JavaScript基礎知識
JavaScript基礎知識 1.標籤組使用 <script charset='utf-8' //設置字元集 defet //使腳本延遲到文檔解析完成,Browser已忽略 language=' ...
- devstack安装openstack newton版本
准备使用devstack安装openstack N版,搞一套开发环境出来.一连整了4天,遇到各种问题,各种错误,一直到第4天下午4点多才算完成. 在这个过程中感觉到使用devstack搭建openst ...
- mybatis运行时错误Illegal argument exception argument type mismatch
使用注解时遇到该错误 使用XML应该也会有相应的错误 解决办法:查看是不是Dao接口的参数列表没有加@Param注解 参数过多时需要该注解指明参数
- bzoj2257 [Jsoi2009]瓶子和燃料 最大公约数
[Jsoi2009]瓶子和燃料 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1449 Solved: 889[Submit][Status][Di ...
- bzoj4198 荷马史诗 哈夫曼编码
逐影子的人,自己就是影子. --荷马 Allison 最近迷上了文学.她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的<荷马史诗>.但是由<奥德赛>和&l ...
- 箱线图(boxplot)简介与举例
简述: 盒图是在1977年由美国的统计学家约翰·图基(John Tukey)发明的.它由五个数值点组成:最小值(min),下四分位数(Q1),中位数(median),上四分位数(Q3),最大值(m ...
- CSS中新属性calc()
CSS3的calc()使用 原文: http://www.w3cplus.com/css3/how-to-use-css3-calc-function.html © w3cplus.com calc( ...
- 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 ...
- C#中 什么是装箱和拆箱
装箱:将值类型包装为引用类型 拆箱:将引用类型转换为值类型 例如 objetct obj = null; obj = ; //装箱 int i = (int) obj; //拆箱