[WebKit]WebKit2多进程机制的解析

在<<WebKit模块化分析>>中说到WebKit2中的多进程模型。多进程模型已经是浏览器的基本架构要素,下面展开分析一下WebKit2中的多进程模型。

协作决定接口,确立责任分工后,对于模块或系统间最重要的事莫过于接口定义,而且是有着简洁明确的定义。对于WebKit2中三个进程中的交互也是相当频繁和多样,如果使用传统的查表法对应解析执行,就会面临巨大的维护成本。WebKit2使用了Encoder和Decoder的概念的很好地将消息解析的工作放到各个功能上。于是提供的公共接口主要关注于消息的分发,而不再是似乎拥有一切的解析执行功能。

WebKit2在多进程接口上Process Launcher、CoreIPC以及WorkQueue构成了核心控制功能 (进程本身的Run Loop自然也是,但非此处的关注点),Encoder/Decoder/FunctionWrapper则是重要的工具类。下面就是对它们进行的学习和分析。

多线程模型的基本架构

先从基本结构看起。多进程模型是在WebKit中实现的,由WebKit中的三个Process分别处理应用主进程,网页处理进程及插件进程。

它们在具体的实现上是基于Process Launcher(WebKit2/UIProcess/Launcher)进行进程管理,再通过Core IPC进行跨进程通讯(IPC)。

   

CoreIPC做为一个公共,且由各个进程共享的模块,日后会被放置到WebCore或WTF中。放在WTF中较为合适,因为它在模块化的角色就是公共功能模块。

Process Launcher具体创建进程的代码需要平台适配,它使用了如下的方式:

      

ProcessLauncher.h和ProcessLauncher.cpp有通用的定义和公共代码的实现。再为不同平台建立不同的单元文件来实现平台差异的部分。负责创建新进程的launchProcess就在其中。

Core IPC也是类似的实现方式。在不同的平台下使用不同的IPC技术。

   

Mach Port是Mac OS下类似pipes技术的实现,而XPC则是新推出一种更为安全的IPC技术,目前国内相关的中文资料并不多,还是要花时研究官方文档(Creating
XPC Services
)和示例代码(SandboxedFetch)。另外网友rainbird还推荐一个对XPC的封装

多进程模型的交互机制


WebKit2中使用来Proxy来与其它进程进行通讯。

  

WebProcessProxy与PluginProcessProxy均会继承自ProcessLauncher::Client, 最后是通过CoreIPC::Connection来完成通讯。因为存在页面上的对应关系,所以消息传递时都会带一个Page ID来标识,这样在共享Web Process时就不会出现混淆的问题。

下图是一个进程通讯的架构, CoreIPC::Connection会通过与其它几个类一起完成进程间消息传递:

   

流程也很容易理解,消息发送者通过Encoder对要发送的信息进行编码,再由Work Queue控制触发发送或接收操作,最后由Decoder进行解码,最后交由目标类解析执行。

消息发送者Sender Class会继承自CoreIPC::MessageSender,而接收者Target Class会继承自CoreIPC::MessageReceiver。

Encoder的实现与平台无关。Work Queue自己会有队列管理机制触发发送操作,再由注册回调函数触发接收操作。无论发送和接收的函数都是由Connection通Function Wrapper提供的,Work Queue就是负责在适当的时机下触发。它在不同平台使用了不同的技术:

  

 关于GCD可以参考一下这篇文章:

   GCD介绍

 Windows下的Thread Pool的说明可以看这里:

   Thread Pool API(MSDN)

下面Work Queue的类图:

    

.dispatch用于发送时,由Connection调用并传入负责发送的函数句柄。

.executeFunction则是用于执行指定的函数指针。

.invalidate则是用于清除当前排入的任务队列。

.platformXXX表示的是对应于各个平台的不同实现。其跨平台模式类似于ProcessLauncher。

.registerXXX与unregisterXXX用于注册和取消处理接收到新消息的回调函数,这个函数也是由Connection指定的( Mac OS下和Windows下的函数名不同)。

下面是Connection与WorkQueue交互示意的序列图:

      

多进程模型中消息数据设计

上面提到了消息在传递过程中外发的消息(outgoing message)和收到的消息(incoming message)都由CoreIPC::Connection类来管理,而且包含

一个编解码的过程。

1. 消息

每一个消息都有一个特定的定义。如下所示,Arguement1和Argument2是两个模板。

        

首先CoreIPC为不同数量参数的消息定义了一套模板,就是ArgumentX, X从1到8,就是最大表示8个参数的消息,模板数据类型指定的是各个参数的类型。

其中一部分使用了修饰模式。不同参数的模板都采用对前一个模板的实现来达到复用的目的。 这种定义方式和一般的查表法是不同的,目的在于消息本身就能表示自己,而不需要再做额外的对消息的映射。

下面是LoadURL消息的定义:

struct LoadURL : CoreIPC::Arguments2<constWTF::String&, constWebKit::SandboxExtension::Handle&>
{

    static const Kind messageID = LoadURLID;

    static CoreIPC::StringReference receiverName() { return messageReceiverName();
}

    staticCoreIPC::StringReference name() { returnCoreIPC::StringReference("LoadURL");
}

    staticconstbool isSync = false;

    typedefCoreIPC::Arguments2<constWTF::String&, constWebKit::SandboxExtension::Handle&>
DecodeType;

    LoadURL(const WTF::String& url, const WebKit::SandboxExtension::Handle&
sandboxExtensionHandle)

        : CoreIPC::Arguments2<const WTF::String&, const WebKit::SandboxExtension::Handle&>(url,
sandboxExtensionHandle)

    {

    }

};

2. Encoder

所谓Encoder就是信息的各个参数组装到一个buffer中的操作。

Encode操作都是通过CoreIPC::MessageEncoder来完成的。它对Message的Message Name, Receiver Name, Destination ID(即Page ID)以及各个参数进行编码。

   

*encode方法有不同类型的副本。

*m_inlineBuffer是字符数组,会比m_buffer动态分配的方式效率要高。

下面Encode的结果(以loadURL为例):

实际发送时就是将这个buffer的内容发送出去。再由接收到使用对应的Decoder解析出来。

   

通过ReceiverName可以创建一个MessageReceiver对象,即此消息的处理对象。就这样,这个消息就确定应当由谁来处理了。

bool MessageReceiverMap::dispatchMessage(Connection* connection, MessageID messageID, MessageDecoder&
decoder)

{

    if (MessageReceiver* messageReceiver = m_globalMessageReceivers.get(decoder.messageReceiverName()))
{

        ASSERT(!decoder.destinationID());

        messageReceiver->didReceiveMessage(connection, messageID, decoder);

        return true;

    }

    ……

}

和传统的处理方式相比,是不是简洁很多。虽然Recevier Names还是要有表存在,但相对常常的switch/case或查表法,它的变动性已经被极大的缩小了,也就降低了日后的维护成本。如果要接受消息处理,只要调用MessageReceiverMap::addMessageReceiver()添加一项Receiver,而参数就是Receiver的名称和类的实例。

再到具体的Message处理对象中处理,对于要接受消息处理的类,会继承自MessageReceiver,使得它们都有一个消息处理的入口。如:
voidWebPage::didReceiveWebPageMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::MessageDecoder& decoder)

{

     ……

    if (decoder.messageName() == Messages::WebPage::LoadURL::name())
{

        CoreIPC::handleMessage<Messages::WebPage::LoadURL>(decoder, this,
&WebPage::loadURL);

        return;

    }

   ……

}

最终通过callMemberFunction()来执行消息对应的函数,即上面代码指定的this和&WebPage::loadURL,decoder中则存储着参数。

3. Function Wrapper

Function Wrapper的功能就是将函数或者某个对象的成员封装成函数指针。在Work Queue与Connection的函数调用过程都是使用Function Wrapper来完成的。

   

Function Wrapper也是一个模板类,用于达到多态以支持不同的函数类型。比如下面支持类成员函数的定义(Functional.h):

template<typename R, typename C>

class FunctionWrapper<R (C::*)()> {

public:

    typedef R ResultType;

    static const bool shouldRefFirstParameter = HasRefAndDeref<C>::value;

    static const bool shouldValidateFirstParameter = true;

    explicit FunctionWrapper(R (C::*function)())

        : m_function(function)

    {

    }

    R operator()(C* c)

    {

        return (c->*m_function)();

    }

private:

    R (C::*m_function)();

};

Function Wrapper对Work Queue要封装就是Connection::sendOutgoingMessage和Connection::dispatchOneMessage()两个分别用于处理发送和收到的消息。

多进程模型的交互序列图示例

以下是一个交互的序列图,方便更好地理解它的设计。

在UI上加载页面时,UIProcess中的WebPageProxy向WebProcess中的WebPage发起loadURL请求。首先会通过调用Connection增加一个message到自己的outgoing message queue中。然后将自身的发送函数(sendOutgoingMessages)发送给WorkQueue对象执行,而不是直接把消息发送到WorkQueue中执行。所以WorkQueue只是一个控件类,而不是接口类。

    

WorkQueue会在适当的时机执行提交的任务,由executeFunction来执行相应的发送函数,将消息通过系统的机制发送出去。

而在WebProcess中,它会先在WorkQueue(不同的实例对象)中注册一个事件处理函数,当事件进来后,它就会被执行到。进行触发Connection对新的消息进行解析并加入到自己的incoming message queue中。

  

Web Process会在自己的Run Loop中要求Connection解析收到的消息,并在解析后调用对应的对象的处理函数。通过callMemberFunction()就最终调用到了WebPage::loadURL()函数。

转载请注明出处:http://blog.csdn.net/horkychen


  


时间: 2024-09-15 02:44:19

[WebKit]WebKit2多进程机制的解析的相关文章

Python多进程机制实例详解

  本文实例讲述了Python多进程机制.分享给大家供大家参考.具体如下: 在以前只是接触过PYTHON的多线程机制,今天搜了一下多进程,相关文章好像不是特别多.看了几篇,小试了一把.程序如下,主要内容就是通过PRODUCER读一个本地文件,一行一行的放到队列中去.然后会有相应的WORKER从队列中取出这些行. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 3

Android异步消息处理机制完全解析-Handler详解

参考资料 - 官方介绍文档 - Android 异步消息处理机制 让你深入理解 Looper.Handler.Message三者关系 - Android异步消息处理机制完全解析,带你从源码的角度彻底理解 - 慕课网课程-Android面试常客Handler详解 如果在非UI线程中更新UI会出现问题吗? 实践: public class MainActivity extends AppCompatActivity { @BindView(R.id.id_tv) TextView idTv; @Ov

[WebKit]WebKit2 API解析

这里是对上一篇<<WebKit模块化分析>>的进一步展开.先从API层开始. API概览 主要类图 WebKit提供了灵活的回调机制用来支持客户端与内核的交互,在API中有一些Set Client类的函数,Client一般就是用于注册针对某一功能的回调函数. 如向WKContext注册history item处理的回调函数,就会使用下面这个结构(WKContext.h): struct WKContextHistoryClient {     int                

Android事件分发机制完全解析,带你从源码的角度彻底理解(下)

记得在前面的文章中,我带大家一起从源码的角度分析了Android中View的事件分发机制,相信阅读过的 朋友对View的事件分发已经有比较深刻的理解了. 还未阅读过的朋友,请先参考 Android事件分发机 制完全解析,带你从源码的角度彻底理解(上) . 那么今天我们将继续上次未完成的话题,从源码的 角度分析ViewGruop的事件分发. 首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区 别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View和子

ThinkPHP的MVC开发机制实例解析_php实例

ThinkPHP是目前国内应用非常广泛的一款MVC开发框架.本文就以实例形式解析ThinkPHP的MVC开发机制.相信会给大家一定的启发作用.具体分析如下: 一.概述: MVC框架解析如下: M  Model层    模型:就是数据库操作类(通过数据库操作类去操作各个表)         V  View层     视图:指模版.       C  Control层  控制器:就是通过控制器来实现模版,模型之间的控制关系. 二.实例分析: 1.ACTION 控制器: 位置 D:\www\aoli\

Docker网络管理机制实例解析+创建自己Docker网络

实例解析Docker网络管理机制(bridge network,overlay network),介绍Docker默认的网络方式,并创建自己的网络桥接方式,将开发的容器添加至自己新建的网络,提高Docker网络安全和通信. 1.给自己的docker (Dcoker1.12GA)容器起个名称 给docker名称的好处是: - 容易记 - 可以通过特殊命令,使得名称可以在容器和容器之间使用 1.1.查看docker已经存在的镜像 wxl@wxl-pc:~$ docker images 1.2.选择t

Oracle中的数据锁定机制全面解析

为了得到最大的性能,一般数据库都有并发机制,不过带来的问题就是数据访问的冲突.为了解决这个问题,大多数数据库用的方法就是数据的锁定. 数据的锁定分为两种方法,第一种叫做悲观锁,第二种叫做乐观锁.什么叫悲观锁呢,悲观锁顾名思义,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住.而乐观锁就是认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让用户返回错误的信息,让用户决定如何去做.

Android6.0 消息机制原理解析_Android

消息都是存放在一个消息队列中去,而消息循环线程就是围绕这个消息队列进入一个无限循环的,直到线程退出.如果队列中有消息,消息循环线程就会把它取出来,并分发给相应的Handler进行处理:如果队列中没有消息,消息循环线程就会进入空闲等待状态,等待下一个消息的到来.在编写Android应用程序时,当程序执行的任务比较繁重时,为了不阻塞UI主线程而导致ANR的发生,我们通常的做法的创建一个子线程来完成特定的任务.在创建子线程时,有两种选择,一种通过创建Thread对象来创建一个无消息循环的子线程:还有一

SqlServer中tempdb的日志机制原理解析及示例分享_MsSql

测试用例 我们分别在用户数据库(testpage),tempdb中创建相似对象t1,#t1,并在tempdb中创建创建非临时表,然后执行相应的insert脚本(用以产生日志),并记录执行时间用以比较用以比较说明tempdb"快" Code 用户数据库testpage use testpage go create table t1 ( id int identity(1,1) not null, str1 char(8000) ) declare @t datetime2=sysutcd