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还有更多的内容,在代码待我们学习,这里暂时总结到这里,后续再补充。

       

  • 相关阅读:
    第一篇 C#模拟http请求抓取数据
    asp.net webService添加头文件验证
    好文记录地址
    关于邮件发送和邮件附件接收方面
    sql 查询时间当前时间少7天
    20190729研究和学习篇
    maven 打包 时出现非法字符: /65279错误
    .netcore开发教程系列之(四)创建web应用程序-razor页面模式
    .netcore开发教程系列之(四)创建web应用程序-Blazor模式
    .netcore开发教程系列之(三)创建web应用程序-Mvc模式
  • 原文地址:https://www.cnblogs.com/danxi/p/7766147.html
Copyright © 2011-2022 走看看