Android-IPC系列(二)

前言

寫完IPC的第一篇我就有點后悔了。。因為binder的水太深了,老羅寫binder寫了十幾萬字。如果深入學習會大量涉及到系統層的知識,甚至SM,Binder驅動都是用c語言寫的。。最近也是看了很多大牛關于binder的文章,可以說對binder的認識又提升了一步。雖然我學的很淺,但是我盡量保證自己寫的都是對的! 來一波binder的最新認識總結吧!~

Android_IPC系列(一)

上一篇文章主要是介紹一些簡單的linux知識,通過AIDL中的生成代碼來理解binder實現跨進程的原理。

任務:

  1. 補充一些Linux知識。
  2. binder實現跨進程通信的實現流程是怎樣的
  3. 簡單介紹binder機制

一些補充知識

1.UserSpace, KernelSpace ,用戶態,內核態

用戶空間和內核空間。內核空間是linux系統的核心,可以訪問受保護資源和所有的硬件設備的權限。內核獨立于應用程序,看起來是系統內部的一個應用。kernel有自己的保護措施,告知其他應用程序他擁有什么權限。所以在邏輯上,將kernal和應用程序抽離成兩塊空間。

當某個應用程序需要訪問內核資源的時候,可以通過系統調用來接入內核。然后內核會來控制這個應用程序對資源的訪問,防止這個程序破壞系統資源,保證安全。這時候就會稱這個進程處于內核態 。當應用程序在跑自己的代碼的時候,就稱為用戶態

2.Binder驅動

Linux系統內部是支持socket,信號量等進程通信的,但是安卓系統在性能和安全兩個方面設計了自己的進程通信方式:Binder。兩個用戶空間想要通信,必須通過內核空間來支持。所以安卓就是將binder驅動作為內核模塊添加到Linux Kernel。Binder驅動運行在kernel空間,支持用戶空間的通信,可以堪稱一個橋梁,所有包含binder的數據包傳輸都會通過binder驅動來完成,無一例外!在binder驅動里面,binder的實體和引用是以節點(struct)的形式存在的,包括server的binder實體,clinet里面擁有的binder引用,內核的0號應用以及SM的binder實體(后兩者后面會提到)。

驅動是Binder通信的核心,系統中所有的Binder實體以及每個實體在各個進程中的引用都登記在驅動中;驅動需要記錄Binder引用->實體之間多對一的關系;為引用找到對應的實體;在某個進程中為實體創建或查找到對應的引用;記錄Binder的歸屬地(位于哪個進程中);通過管理Binder的強/弱引用創建/銷毀Binder實體等等。

3.ServiceManager, Binder,Clinet, Server

在Binder的機制中,SM, Clinet, Server以及上面提到的Binder驅動是很重要的四個部分,而binder就可以看成是這四個部件之間溝通的管道。但是binder在每個部件里面的形態,功能是完全不同的。宏觀來看,binder可以看成是安卓跨進程通信的方式,工具,協議。微觀的來看,binder可以看成各個部件里面重要的結構,類,binder可以看成binder驅動里面的紅黑樹數據結構,binder可以看成進程之間傳輸的數據包。binder作為膠水,將不同的進程粘合在一起,模糊了進程隔離。

ServiceManager是獨立于client, server的系統進程。是由zygote進程fork出來的。它在IPC通信中的作用是作為"通訊錄"。所有的server里面的binder實體會先在SM里面注冊自己的信息,key是binder的名字(獨一無二),在AIDL的生成代碼里面你會看到這個玩意:

private static final java.lang.String DESCRIPTOR ="com.example.zane.ipc_test.IBookManager";

value就是這個binder的引用,這樣就把這個binder的信息保存下來了。也就是說client需要通過SM,以名字來去得到server的binder引用。然后通過這個引用去操作server端的binder。這里也體現了binder中面向對象的設計理念。clinet自己并不知道自己獲得的這個binder引用是真的實體還是什么假的實體,它只會去拿著自己獲得的引用去操作。并且server中binder實體的引用會在SM,和所有需要跟自己通信的clinet里面存在。在這里,你可以把引用看成指針,或者代理對象。

client和server就是客戶端和服務端。

在服務端:如果你熟悉AIDL進行進程間通信的流程或者看過我上篇博客,你應該對binder的存在形式有所體會。首先是一個aidl類型的接口,定義了binder的所有功能函數。并且這些功能函數需要被編號,因為服務端是通過解析客戶端傳遞過來的數據包中的函數編號來知道自己應該去調用什么函數。在Stub類中有這么幾行代碼(函數編號):

static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

我們知道Stub就是Binder在Server的實體,里面實現了很多函數,onTransact()就是來分析客戶端的請求類型的函數,然后調用相應的函數,并且將返回結果放在數據包中返回給客戶端。

在客戶端:同樣的,我們在客戶端也需要去實現Binder,只不過這個binder是SM轉發給我們的,如果客戶端和服務端在同一個進程,那么就會返回binder實體,如果不在同一個進程就會返回binder的代理。由于binder代理和binder實體都是實現了AIDL接口的類。所以客戶端看不出來這個binder是實體或是引用。如果你做過IPC通信,你會知道以上的過程是通過asInterface(IBiner binder)這個方法實現的。


實現流程

直接上一張我自己畫的簡介圖!

簡介圖
簡介圖

可以很清楚的看出,binder驅動是整個流程的核心!

1.Server將自己的binder通過binder驅動在SM中進行注冊。

2.binder驅動會建立一個binder實體的數據節點和實體的引用。

3.Binder驅動再把名字和引用打包發給SM。

4.Client通過binder驅動拿著他所需要的binder名字向SM請求binder。

5.SM在自己的查找表里面找到對應的引用之后再通過binder驅動返回給client。

所有的系統服務在SystemServer進程深沉之后就會被建立并且注冊在ServiceManager里面,開發者也可以開發自定義的服務并且注冊在ServiceManager里面成為系統級別的服務,前提是這個包含服務的應用必須是system用戶并且帶了system簽名(系統安全),否則是不能隨意注冊的!

好了,再來一張我在一位大牛博客里面截下來的圖片,這個流程描述就更具體了:

ipc2-2.png

其實就是多了binder驅動里面的一些數據結構節點和一個叫0號引用的東西。那么這個0號引用是什么呢?我們知道SM, Client, Server都是運行在三個不同的進程的。那么第一步Server要向SM注冊自己binder的信息,那么這里已經涉及到了跨進程通信了。那么這個進程通信是怎么實現的呢?通過上圖可以看到,所有的Client里面的0號引用都指向了SM里面的binder。也就是說SM和其他所有Server通信的過程都是SM先通過特殊的命令在biner驅動中建立了自己binder的實體節點,并且其他所有地方的0號引用都默認留給SM的binder實體引用。


結束語

上一篇文章講的AIDL生成代碼的分析,本來準備第二篇總結一下AIDL的使用。后來通過不斷的學習覺得這篇文章講的東西更值得總結!

總之,binder要學習的東西還是很多。比如binder協議,binder傳輸數據包類型,binder在驅動里面的數據結構,緩存和線程池管理等等…Binder在安全性,效率性都優于Linux系統默認支持的IPC通信方式,擁有面向對象的設計原理。

未經博主同意,不得轉載該篇文章

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容