zoukankan      html  css  js  c++  java
  • 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、消息的声明

    1 IPC_MESSAGE_ROUTED2(FrameHostMsg_MyMessage, GURL, int)

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

    1 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的创建:

     

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

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

     1 ChannelMojo::ChannelMojo(
     2     mojo::ScopedMessagePipeHandle handle,
     3     Mode mode,
     4     Listener* listener,
     5     const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner)
     6     : task_runner_(ipc_task_runner),
     7       pipe_(handle.get()),
     8       listener_(listener),
     9       weak_factory_(this) {
    10   weak_ptr_ = weak_factory_.GetWeakPtr();
    11   bootstrap_ = MojoBootstrap::Create(std::move(handle), mode, ipc_task_runner);
    12 }

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

     1 class MojoBootstrapImpl : public MojoBootstrap {
     2  public:
     3   MojoBootstrapImpl(
     4       mojo::ScopedMessagePipeHandle handle,
     5       const scoped_refptr<ChannelAssociatedGroupController> controller)
     6       : controller_(controller),
     7         associated_group_(controller),
     8         handle_(std::move(handle)) {}
     9 
    10   ~MojoBootstrapImpl() override {
    11     controller_->ShutDown();
    12   }
    13 
    14  private:
    15   void Connect(mojom::ChannelAssociatedPtr* sender,
    16                mojom::ChannelAssociatedRequest* receiver) override {
    17     controller_->Bind(std::move(handle_));
    18     controller_->CreateChannelEndpoints(sender, receiver);
    19   }
    20 
    21  。。。
    22 }

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

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

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

    1 IPC::ChannelProxy* GetChannel() = 0;

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

     1 class CONTENT_EXPORT RenderProcessHostImpl
     2     : public RenderProcessHost,
     3       public ChildProcessLauncher::Client,
     4       public ui::GpuSwitchingObserver,
     5       public mojom::RouteProvider,
     6       public mojom::AssociatedInterfaceProvider,
     7       public mojom::RendererHost {
     8       ...
     9       IPC::ChannelProxy* GetChannel() override;
    10       ...
    11 }

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

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

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

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

    三、发送者和接收者

        1、发送者

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

     1 class Message;
     2 
     3 class IPC_EXPORT Sender {
     4  public:
     5   // Sends the given IPC message.  The implementor takes ownership of the
     6   // given Message regardless of whether or not this method succeeds.  This
     7   // is done to make this method easier to use.  Returns true on success and
     8   // false otherwise.
     9   virtual bool Send(Message* msg) = 0;
    10 
    11  protected:
    12   virtual ~Sender() {}
    13 };

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

    1 class IPC_EXPORT ChannelProxy : public Sender {
    2 
    3 }

    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) = 0;
    
      ...
    };

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

    1 class IPC_EXPORT MessageRouter : public Listener, public Sender { 
    2     ...
    3 }

    还有子线程实例也是Listener:

    1 class CONTENT_EXPORT ChildThreadImpl
    2     : public IPC::Listener,
    3       virtual public ChildThread,
    4       private base::FieldTrialList::Observer,
    5       public mojom::RouteProvider,
    6       public mojom::AssociatedInterfaceProvider,
    7       public mojom::ChildControl {
    8     ...
    9  }

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

       

  • 相关阅读:
    android 中文 api (43) —— Chronometer
    SVN客户端清除密码
    Android 中文 API (35) —— ImageSwitcher
    Android 中文API (46) —— SimpleAdapter
    Android 中文 API (28) —— CheckedTextView
    Android 中文 API (36) —— Toast
    Android 中文 API (29) —— CompoundButton
    android 中文 API (41) —— RatingBar.OnRatingBarChangeListener
    Android 中文 API (30) —— CompoundButton.OnCheckedChangeListener
    Android 中文 API (24) —— MultiAutoCompleteTextView.CommaTokenizer
  • 原文地址:https://www.cnblogs.com/danxi/p/7766147.html
Copyright © 2011-2022 走看看