全书地址
Chromium中文文档 for https://www.chromium.org/developers/design-documents
持续更新ing,欢迎star
gitbook地址:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//
github地址: https://github.com/ahangchen/Chromium_doc_zhjava
Chromium有一个多进程架构,这意味着咱们有许多须要互相交流的进程。咱们的主要跨进程交流元素是命名管道。在Linux和OS X上,咱们使用socketpair()。每一个渲染器进程能够分配到一个命名管道来跟浏览器进程交流。这些管道是用异步方式使用的,确保没有哪一个端会等待另外一个端。git
想要获得如何编写安全的IPC端点的知识,请查看IPC安全要点.github
在浏览器中,与渲染器的交流是经过一个独立的I/O线程完成的。来自或者去往view的消息须要使用一个ChannelProxy代理到主线程。这种方案的优势是,资源请求(好比网页等),这种最常常且极其关注性能的消息,能够整个的在I/O线程中处理,不会阻塞用户界面。这些经过使用Channel::MessageFilter(由RenderProcessHost插入channel)来完成。这个过滤器运行在I/O线程里,拦截资源请求信息,将它们直接转发到资源分发主机。查看多进程资源加载获取更多关于资源加载的信息。浏览器
每一个渲染器也有一个线程管理交流(在这个例子里,是主线程),而大多数渲染和大多数处理发生在另外一个线程里(查看多进程架构的那个图表)。大多数消息经过主渲染线程从浏览器发送给WebKit线程,反之亦然。这个额外的线程是用于支持同步的渲染器到浏览器的消息(参考下面的“同步消息”)。安全
咱们有两种基本的消息类型:”路由“和”控制“。控制消息由建立管道的类处理,有时候这个类容许其余人经过一个MessageRouter对象接收消息,其余监听器能够经过这个对象注册和接收有着惟一管道id的消息。cookie
例如,渲染时,控制消息没有消息指定目标view,会被RenderProcess(渲染器)或者RenderProcessHost(浏览器)处理。来自资源的请求或者修改剪贴板的请求是没有目标view的,因此是控制消息。一个路由消息的例子是,要求一个view绘制一个区域的消息。架构
路由消息曾经被用于从指定的RenderViewHost获取消息。然而,技术上,任何类能够经过使用RenderProcessHost::GetNextRoutingID接收路由消息,并用RenderProcessHost::AddRoute注册它本身这个消息。如今,RenderFrameHost和RenderViewHost有了他们本身的路由ID了。异步
消息是不是独立类型在于,消息是从浏览器发送到渲染器,仍是从渲染器到浏览器。从浏览器到渲染器的被称为View消息,由于它们被发送给RenderViewHost。从渲染器发送到浏览器的消息叫作ViewHost消息,由于他们被发送给RenderViewHost。你会注意到render_messages_internal.h里定义的消息被分为两类。socket
插件也有独立的进程。像渲染消息那样,PluginProcess消息(从浏览器发送到插件进程)和PluginProcessHost消息(从插件进程发送到浏览器)。这些消息都定义在plugin_messages_internal.h里。自动化消息(用于控制浏览器作UI测试)经过相同的方式完成。
特殊的宏用于声明消息。渲染器和浏览器间发送的消息都声明在render_messages_internal.h里。有两个部分,一个是发送到渲染器的View消息,一个是发送到浏览器的ViewHost消息。
若是要声明一个从渲染器发送到浏览器(一个ViewHost消息)的消息,而且指定一个view(路由)包含一个url和一个整数做为参数,这样写:
IPC_MESSAGE_ROUTED2(ViewHostMsg_MyMessage, GURL, int)
若是要声明一个从浏览器发往渲染器的控制消息(一个View消息),而且不指定目标view(控制),不包含参数,这样写:
IPC_MESSAGE_CONTROL0(ViewMsg_MyMessage)
参数经过ParamTraits模板序列化或者反序列化到消息体中。这种模板的具体化在ipc_message_utils.h中提供给大多数常见的类型。若是你定义了你本身的类型,你也须要为它定义你本身的ParamTraits具体形式。
有时候,一条消息有太多的值了,无法合理得放到消息里。这种状况下,咱们定义一个独立的结构来存放这些值。例如,对于FrameMsg_Navigate消息,在navigation_params.h中,定义了CommonNavigationParams结构。frame_messages.h用IPC_STRUCT_TRAITS类型的宏定义了这个结构的具体ParamTraits。
你经过“channel(通道)”发送消息(往下看)。在浏览器里,RenderProcessHost包含了用于从浏览器UI线程发送消息到渲染器的channel。为了方便,RenderWidgetHost(RenderViewHost的基类)提供了一个Send函数。
消息由指针发送,并将在它们分发后由IPC层删除。所以,一旦你发现合适的Send函数,尽管带着一条新消息去调用它:
Send(new ViewMsg_StopFinding(routing_id_));
注意,你必须按顺序指定路由ID,让消息可以路由到接收端正确的View/ViewHost。RenderWidgetHost(RenderViewHost的基类)和RenderWidget(RenderViewHost的基类)都有GetRoutingID()成员函数给你使用。
消息由对IPC::Listener的实现来处理,其中最重要的函数是OnMessageReceived。咱们有大量的宏来简化这个函数中的消息处理,这个最好能够用例子来阐述:
MyClass::OnMessageReceived(const IPC::Message& message) { IPC_BEGIN_MESSAGE_MAP(MyClass, message) // Will call OnMyMessage with the message. The parameters of the message will be unpacked for you. IPC_MESSAGE_HANDLER(ViewHostMsg_MyMessage, OnMyMessage) ... IPC_MESSAGE_UNHANDLED_ERROR() // This will throw an exception for unhandled messages. IPC_END_MESSAGE_MAP() } // This function will be called with the parameters extracted from the ViewHostMsg_MyMessage message. MyClass::OnMyMessage(const GURL& url, int something) { ... }
你也可使用IPC_DEFINE_MESSAGE_MAP来实现本身的函数定义。在这个例子里,不要指定消息变量名,它会在给定的类上声明一个OnMessageReceived函数,并实现之。
其余宏:
IPC_MESSAGE_FORWARD(ViewHostMsg_MyMessage, some_object_pointer, SomeObject::OnMyMessage)
IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_MyMessage, printf("Hello, world, I got the message."))
IPC中的安全漏洞有着严重的后果(文件盗取,沙箱逃逸,远程代码执行),查看咱们的IPC安全文档以获取如何避免常见陷阱的一些提示。
IPC::Channel()(定义在ipc/ipc_channel.h里)定义了经过管道交流的方法。IPC::SyncChannel提供了额外的功能用于同步等待一些消息的响应(正以下面的“同步消息”描述的,渲染器进程使用了这个特性,但浏览器进程不会这样作)。
通道不是线程安全的,咱们一般但愿用通道在另外一个线程里发送消息。例如,当UI线程但愿发送消息时,它必须经过I/O线程。为此,咱们使用IPC::ChannelProxy。它有着与正常通道对象相似的API,但它把消息代理到另外一个线程去发送,而在收到这些消息时,把消息代理回原来的线程。这容许你的对象(一般是在UI线程中)在通道线程(一般是在I/O线程中)安装一个IPC::ChannelProxy::Listener,以此从代理的消息中过滤掉一些消息。咱们使用这个特性去作资源请求以及其余能够直接在I/O线程处理的请求。RenderProcessHost安装一个RenderMessageFilter对象执行这种过滤。
有些消息应该从渲染器的角度来同步。这大多数时候发生在,有一个支持返回值的WebKit调用,但咱们必须在浏览器中执行这个调用。这种消息的例子是拼写检查以及在javaScript中获取cookie。同步浏览器到渲染器的IPC是不容许的,以此避免在一个潜在的片断渲染器中阻塞用户界面。
警告: 不要在UI线程处理任何同步消息!你必须在I/O线程中处理他们。不然,应用程序可能由于插件等待UI线程的同步绘制而陷入死锁,而渲染器等待浏览器同步消息时也会有一些阻塞。
同步消息用IPC_SYNC_MESSAGE_*这样的宏来声明。这些宏有输入,也有返回值()(非同步消息没有返回参数的概念)。对于一个有着两个输入参数和一个返回参数的控制函数,你应该在宏的名字中插入“2_1”:
IPC_SYNC_MESSAGE_CONTROL2_1(SomeMessage, // Message name GURL, //input_param1 int, //input_param2 std::string); //result
相似的,你也可让消息路由到view,这种状况下你须要把“CONTROL”换成“ROUTED”,获得IPC_SYNC_MESSAGE_*。你也能够没有输入或返回参数。没有返回参数经常使用于渲染器必须等待浏览器完成某些操做但不须要结果时。咱们在某些打印和剪贴板操做使用这种特性。
当WebKit线程分发出一个同步IPC请求时,请求对象(继承自IPC::SyncMessage)会在渲染器中经过IPC::SyncChannel对象分发给主线程。全部同步的消息也是经过它发送的。同步通道在接收到同步消息时,会阻塞调用线程,只有当收到回复时,才会解除阻塞。
在WebKit线程等待同步请求时,主线程仍然会从浏览器进程接收消息。这些消息会添加到WebKit线程里,等到WebKit线程被唤醒时处理它们。当同步消息回复被接收时,这个线程会解除阻塞。注意这意味着同步消息回复能够不按顺序处理。
同步消息和正常的消息用一样的方式,带着赋予构造器的输出参数发送出去,例如:
const GURL input_param("http://www.google.com/"); std::string result; content::RenderThread::Get()->Send(new MyMessage(input_param, &result)); printf("The result is %s\n", result.c_str());
同步消息和异步消息使用相同的IPC_MESSAGE_HANDLER等宏来分发消息。消息处理函数与消息构造器有着相同的函数签名,这个函数会简单把输出写到输出参数中。在上面的消息里,你能够添加:
IPC_MESSAGE_HANDLER(MyMessage, OnMyMessage)
到OnMessageReceived函数,而后这样写:
void RenderProcessHost::OnMyMessage(GURL input_param, std::string* result) { *result = input_param.spec() + " is not available"; }
若是运行崩溃了,而且此时你有消息的类型,你能够把它转为消息名。这种消息类型是一个32位值,高16位是类,低16位是ID。类基于ipc/ipc_message_start.h中的枚举值,id基于定义消息的文件中的行号。这意味着你须要获取准确的Chromium版本以获取消息名。
一个554011中的例子是Chromium ad0950c1ac32ef02b0b0133ebac2a0fa4771cf20 版0x1c0098中。类0x1c,意味着行40,匹配ChildProcessMsgStart。ChildProcessMsgStart消息在content/common/child_process_messages.h中,而0x98行,即152行,对应的IPC是ChildProcessHostMsg_ChildHistogramData.
当你在处理content::RenderProcessHostImpl::OnBadMessageReceived致使的crash时,这项技术很是有用。