chromium源碼閱讀--進程間通訊(IPC)

     第一篇就有提到Chromium是目前默認是採用多進程架構,固然,chromium有singe-process的版本。html

     多進程與多線程的區別,確實有不少能夠講的,個人另外一篇博客也講了一些 (Linux 進程,線程),這裏是從瀏覽器的角度來講,若是是多線程,若是一個線程崩潰,影響了整個瀏覽器的使用,由於在如今的網頁標準更新了不少個版本,會有不一樣標準的頁面在網絡上,極大可能出現解析,渲染,插件等問題,那麼對於用戶來講,體驗就會差不少了,瀏覽一個頁面出問題,就要重啓瀏覽器。而多進程則能夠避免此問題,render進程崩潰只會影響當前的tab。linux

    嗯,上面說了那麼多,就是爲了說,多進程之間就須要進程通訊來協做,而chromium的進程間通訊是很是繁雜的,如何處理這個是咱們須要瞭解的關鍵。bootstrap

   那麼本質的問題就是:瀏覽器

         一、發那些消息(Message Type)安全

         二、消息通道是怎麼創建的 (Message Channel)網絡

         三、發送者和接收者(Sender,Listener)多線程

OK,咱一個個來。架構

 

1、 Message Type less

     主要分爲2類:「routed」 和 「control」。socket

     一、routed消息

         主要是用來給某個RenderViewHost對象發送消息的。不過,任何類均可以經過GetNextRoutingID 和 AddRoute 註冊,就能接收routed消息。

     二、control消息

          control消息有建立pipe的類處理,固然這些類也能夠接收routed消息。好比,請求資源或修改剪貼板不是特定於視圖的,因此是控制消息。

     三、消息的聲明

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字樣。

 

2、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來完成了。

 

3、發送者和接收者

    一、發送者

    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 }

二、接收者

    一樣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還有更多的內容,在代碼待咱們學習,這裏暫時總結到這裏,後續再補充。

相關文章
相關標籤/搜索