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