全書地址
Chromium中文文檔 for https://www.chromium.org/developers/design-documents
持續更新ing,歡迎star
gitbook地址:https://ahangchen.gitbooks.io/chromium_doc_zh/content/zh//
github地址: https://github.com/ahangchen/Chromium_doc_zhjava
Chromium有一個多進程架構,這意味着咱們有許多須要互相交流的進程。咱們的主要跨進程交流元素是命名管道。在Linux和OS X上,咱們使用socketpair()。每一個渲染器進程能夠分配到一個命名管道來跟瀏覽器進程交流。這些管道是用異步方式使用的,確保沒有哪一個端會等待另外一個端。git
想要獲得如何編寫安全的IPC端點的知識,請查看IPC安全要點.github
在瀏覽器中,與渲染器的交流是經過一個獨立的I/O線程完成的。來自或者去往view的消息須要使用一個ChannelProxy代理到主線程。這種方案的優勢是,資源請求(好比網頁等),這種最常常且極其關注性能的消息,能夠整個的在I/O線程中處理,不會阻塞用戶界面。這些經過使用Channel::MessageFilter(由RenderProcessHost插入channel)來完成。這個過濾器運行在I/O線程裏,攔截資源請求信息,將它們直接轉發到資源分發主機。查看多進程資源加載獲取更多關於資源加載的信息。瀏覽器
每一個渲染器也有一個線程管理交流(在這個例子裏,是主線程),而大多數渲染和大多數處理髮生在另外一個線程裏(查看多進程架構的那個圖表)。大多數消息經過主渲染線程從瀏覽器發送給WebKit線程,反之亦然。這個額外的線程是用於支持同步的渲染器到瀏覽器的消息(參考下面的「同步消息」)。安全
咱們有兩種基本的消息類型:」路由「和」控制「。控制消息由建立管道的類處理,有時候這個類容許其餘人經過一個MessageRouter對象接收消息,其餘監聽器能夠經過這個對象註冊和接收有着惟一管道id的消息。cookie
例如,渲染時,控制消息沒有消息指定目標view,會被RenderProcess(渲染器)或者RenderProcessHost(瀏覽器)處理。來自資源的請求或者修改剪貼板的請求是沒有目標view的,因此是控制消息。一個路由消息的例子是,要求一個view繪製一個區域的消息。架構
路由消息曾經被用於從指定的RenderViewHost獲取消息。然而,技術上,任何類能夠經過使用RenderProcessHost::GetNextRoutingID接收路由消息,並用RenderProcessHost::AddRoute註冊它本身這個消息。如今,RenderFrameHost和RenderViewHost有了他們本身的路由ID了。異步
消息是不是獨立類型在於,消息是從瀏覽器發送到渲染器,仍是從渲染器到瀏覽器。從瀏覽器到渲染器的被稱爲View消息,由於它們被髮送給RenderViewHost。從渲染器發送到瀏覽器的消息叫作ViewHost消息,由於他們被髮送給RenderViewHost。你會注意到render_messages_internal.h裏定義的消息被分爲兩類。socket
插件也有獨立的進程。像渲染消息那樣,PluginProcess消息(從瀏覽器發送到插件進程)和PluginProcessHost消息(從插件進程發送到瀏覽器)。這些消息都定義在plugin_messages_internal.h裏。自動化消息(用於控制瀏覽器作UI測試)經過相同的方式完成。
特殊的宏用於聲明消息。渲染器和瀏覽器間發送的消息都聲明在render_messages_internal.h裏。有兩個部分,一個是發送到渲染器的View消息,一個是發送到瀏覽器的ViewHost消息。
若是要聲明一個從渲染器發送到瀏覽器(一個ViewHost消息)的消息,而且指定一個view(路由)包含一個url和一個整數做爲參數,這樣寫:
IPC_MESSAGE_ROUTED2(ViewHostMsg_MyMessage, GURL, int)
若是要聲明一個從瀏覽器發往渲染器的控制消息(一個View消息),而且不指定目標view(控制),不包含參數,這樣寫:
IPC_MESSAGE_CONTROL0(ViewMsg_MyMessage)
參數經過ParamTraits模板序列化或者反序列化到消息體中。這種模板的具體化在ipc_message_utils.h中提供給大多數常見的類型。若是你定義了你本身的類型,你也須要爲它定義你本身的ParamTraits具體形式。
有時候,一條消息有太多的值了,無法合理得放到消息裏。這種狀況下,咱們定義一個獨立的結構來存放這些值。例如,對於FrameMsg_Navigate消息,在navigation_params.h中,定義了CommonNavigationParams結構。frame_messages.h用IPC_STRUCT_TRAITS類型的宏定義了這個結構的具體ParamTraits。
你經過「channel(通道)」發送消息(往下看)。在瀏覽器裏,RenderProcessHost包含了用於從瀏覽器UI線程發送消息到渲染器的channel。爲了方便,RenderWidgetHost(RenderViewHost的基類)提供了一個Send函數。
消息由指針發送,並將在它們分發後由IPC層刪除。所以,一旦你發現合適的Send函數,儘管帶着一條新消息去調用它:
Send(new ViewMsg_StopFinding(routing_id_));
注意,你必須按順序指定路由ID,讓消息可以路由到接收端正確的View/ViewHost。RenderWidgetHost(RenderViewHost的基類)和RenderWidget(RenderViewHost的基類)都有GetRoutingID()成員函數給你使用。
消息由對IPC::Listener的實現來處理,其中最重要的函數是OnMessageReceived。咱們有大量的宏來簡化這個函數中的消息處理,這個最好能夠用例子來闡述:
MyClass::OnMessageReceived(const IPC::Message& message) { IPC_BEGIN_MESSAGE_MAP(MyClass, message) // Will call OnMyMessage with the message. The parameters of the message will be unpacked for you. IPC_MESSAGE_HANDLER(ViewHostMsg_MyMessage, OnMyMessage) ... IPC_MESSAGE_UNHANDLED_ERROR() // This will throw an exception for unhandled messages. IPC_END_MESSAGE_MAP() } // This function will be called with the parameters extracted from the ViewHostMsg_MyMessage message. MyClass::OnMyMessage(const GURL& url, int something) { ... }
你也可使用IPC_DEFINE_MESSAGE_MAP來實現本身的函數定義。在這個例子裏,不要指定消息變量名,它會在給定的類上聲明一個OnMessageReceived函數,並實現之。
其餘宏:
IPC_MESSAGE_FORWARD(ViewHostMsg_MyMessage, some_object_pointer, SomeObject::OnMyMessage)
IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_MyMessage, printf("Hello, world, I got the message."))
IPC中的安全漏洞有着嚴重的後果(文件盜取,沙箱逃逸,遠程代碼執行),查看咱們的IPC安全文檔以獲取如何避免常見陷阱的一些提示。
IPC::Channel()(定義在ipc/ipc_channel.h裏)定義了經過管道交流的方法。IPC::SyncChannel提供了額外的功能用於同步等待一些消息的響應(正以下面的「同步消息」描述的,渲染器進程使用了這個特性,但瀏覽器進程不會這樣作)。
通道不是線程安全的,咱們一般但願用通道在另外一個線程裏發送消息。例如,當UI線程但願發送消息時,它必須經過I/O線程。爲此,咱們使用IPC::ChannelProxy。它有着與正常通道對象相似的API,但它把消息代理到另外一個線程去發送,而在收到這些消息時,把消息代理回原來的線程。這容許你的對象(一般是在UI線程中)在通道線程(一般是在I/O線程中)安裝一個IPC::ChannelProxy::Listener,以此從代理的消息中過濾掉一些消息。咱們使用這個特性去作資源請求以及其餘能夠直接在I/O線程處理的請求。RenderProcessHost安裝一個RenderMessageFilter對象執行這種過濾。
有些消息應該從渲染器的角度來同步。這大多數時候發生在,有一個支持返回值的WebKit調用,但咱們必須在瀏覽器中執行這個調用。這種消息的例子是拼寫檢查以及在javaScript中獲取cookie。同步瀏覽器到渲染器的IPC是不容許的,以此避免在一個潛在的片斷渲染器中阻塞用戶界面。
警告: 不要在UI線程處理任何同步消息!你必須在I/O線程中處理他們。不然,應用程序可能由於插件等待UI線程的同步繪製而陷入死鎖,而渲染器等待瀏覽器同步消息時也會有一些阻塞。
同步消息用IPC_SYNC_MESSAGE_*這樣的宏來聲明。這些宏有輸入,也有返回值()(非同步消息沒有返回參數的概念)。對於一個有着兩個輸入參數和一個返回參數的控制函數,你應該在宏的名字中插入「2_1」:
IPC_SYNC_MESSAGE_CONTROL2_1(SomeMessage, // Message name GURL, //input_param1 int, //input_param2 std::string); //result
相似的,你也可讓消息路由到view,這種狀況下你須要把「CONTROL」換成「ROUTED」,獲得IPC_SYNC_MESSAGE_*。你也能夠沒有輸入或返回參數。沒有返回參數經常使用於渲染器必須等待瀏覽器完成某些操做但不須要結果時。咱們在某些打印和剪貼板操做使用這種特性。
當WebKit線程分發出一個同步IPC請求時,請求對象(繼承自IPC::SyncMessage)會在渲染器中經過IPC::SyncChannel對象分發給主線程。全部同步的消息也是經過它發送的。同步通道在接收到同步消息時,會阻塞調用線程,只有當收到回覆時,纔會解除阻塞。
在WebKit線程等待同步請求時,主線程仍然會從瀏覽器進程接收消息。這些消息會添加到WebKit線程裏,等到WebKit線程被喚醒時處理它們。當同步消息回覆被接收時,這個線程會解除阻塞。注意這意味着同步消息回覆能夠不按順序處理。
同步消息和正常的消息用一樣的方式,帶着賦予構造器的輸出參數發送出去,例如:
const GURL input_param("http://www.google.com/"); std::string result; content::RenderThread::Get()->Send(new MyMessage(input_param, &result)); printf("The result is %s\n", result.c_str());
同步消息和異步消息使用相同的IPC_MESSAGE_HANDLER等宏來分發消息。消息處理函數與消息構造器有着相同的函數簽名,這個函數會簡單把輸出寫到輸出參數中。在上面的消息裏,你能夠添加:
IPC_MESSAGE_HANDLER(MyMessage, OnMyMessage)
到OnMessageReceived函數,而後這樣寫:
void RenderProcessHost::OnMyMessage(GURL input_param, std::string* result) { *result = input_param.spec() + " is not available"; }
若是運行崩潰了,而且此時你有消息的類型,你能夠把它轉爲消息名。這種消息類型是一個32位值,高16位是類,低16位是ID。類基於ipc/ipc_message_start.h中的枚舉值,id基於定義消息的文件中的行號。這意味着你須要獲取準確的Chromium版本以獲取消息名。
一個554011中的例子是Chromium ad0950c1ac32ef02b0b0133ebac2a0fa4771cf20 版0x1c0098中。類0x1c,意味着行40,匹配ChildProcessMsgStart。ChildProcessMsgStart消息在content/common/child_process_messages.h中,而0x98行,即152行,對應的IPC是ChildProcessHostMsg_ChildHistogramData.
當你在處理content::RenderProcessHostImpl::OnBadMessageReceived致使的crash時,這項技術很是有用。