Android Binder驅動的工作機制之要旨
最近,看了不少Android核心分析的書籍、文章及Android來源程式。感覺自己對Android Binder的工作機制算是有了個徹底的理解。
但是,自己是花了很多時間和精力之後才達到這一點的。對於大多數人來說,恐怕不會象我這樣願意花這麼大的代價就為了弄明白這一點東西。所以,我想儘可能簡要的介紹一下Android Binder驅動的工作機制原理,以使大家不必花太多時間也能弄明白其要旨。
一、我們先介紹一下Binder Driver所要解決的問題。
Binder driver所要解決的是運行在同一個Android作業系統中的不同進程之間的小規模資料的傳遞問題。
注意,對於不同進程之間的大規模資料的傳遞問題,Android是用Android Shared Memory Driver來解決的。Binder Driver是針對小規模的資料傳遞的(比如幾十位元組到幾十KB的資料)。
Android Driver是整個Android Binder系統的核心部分。Android Binder系統的目標是提供一個運行在同一個Android作業系統中的不同處理序間通訊的物件導向的API,類似於Microsoft DCOM或者CORBA。為了提供這樣的物件導向的API, Android需要一種高效的處理序間通訊機制,這就是Android driver存在的目的。
二、那麼,為什麼不直接用Linux的共用記憶體機制來在進程間傳遞資料呢?
這是因為,除了傳遞資料之外,Android還需要這個Driver支援以下特性。
1. 安全機制:只有特定的使用者和進程才能訪問這塊資料。Linux共用記憶體不支援這樣的機制。
2. 引用計數:只有Client進程不再需要了,其對應的Server進程才能釋放相應的記憶體。Linux共用記憶體也不支援這樣的機制。
3. 另外,Android Binder driver還支援誇進程的描述符傳遞。這樣,一個進程可以直接讀另一個進程開啟的檔案,而不需要將檔案內容讀到記憶體後再複製給另一個進程。
三、Linux進程的虛擬記憶體空間
下面以x86 CPU的Linux實現為例,回顧一下Linux進程的虛擬記憶體空間的相關知識。
1. 每個進程有4G的虛擬記憶體空間
2.這4G空間分為兩部分。0~3G為使用者態空間(User Space),3~4G為核心態空間(Kernel Space)。
3.對於0~3G的使用者態空間,對於不同的進程,Linux會將其映射到不同的實體記憶體。比如,進程A的虛擬位址0x01234567所指向的物理地址與進程B的虛擬位址0x01234567所指向的物理地址是不同的。這就是所謂的進程隔離。
4.對於3~4G的核心態空間,對於所有的進程,Linux會將其映射到相同的實體記憶體。換句話說,進程A的虛擬位址0xC1234567與進程B的虛擬位址0xC1234567指向的是同一個物理地址。這部分虛擬記憶體空間,使用者態的進程是不能訪問的,只有切換到核心態的進程才能訪問一個進程的3~4G的核心態空間。
四、將資料從進程A傳遞到進程B的確切含義
所謂將資料從進程A傳遞到進程B,就是將進程A的某塊使用者空間(0~3G)的內容複寫到進程B的某塊使用者空間(0~3G)的記憶體中。
因此,為了將資料從一個進程A複製到另一個進程B,我們需要:
1. 切換到核心態。因為只有核心態才能訪問所有進程的使用者態空間。
2. 將資料從進程A的使用者態空間讀出。
3. 在進程B的使用者態空間中找一塊記憶體空間,為其分配實體記憶體,然後將資料複製到該實體記憶體處。
4. 然後,進程B就可以從其使用者空間讀到資料了。
五、Binder Driver是如何在進程間傳遞資料的
其實,Binder driver要做的事情就是上面我們所列出的那幾個步驟。
不過,對於每個進程,Binder driver會在進程開啟binder driver後,通過ioctl系統調用為每個進程預先找一塊使用者態和核心態的記憶體空間,做為Binder buffer使用。
請注意,對於一個進程而言,其Binder buffer在使用者態和核心態的記憶體被映射到了同樣的實體記憶體地址。
下面我們看看利用Binder進行通訊的兩個進程之間是如何傳遞資料的:
1. Client進程有一塊記憶體內容需要傳遞給Server進程
2. 切換到核心態
3. Binder driver讀Client進程的使用者態空間(0~3G)的這塊記憶體內容
4. Binder driver將讀出的內容複寫到Server進程的Binder buffer中
5. 然後,當Server進程執行時,就能夠在其使用者態空間(0~3G)讀傳過來的內容了。因為Binder Buffer也被映射到了Server進程的使用者態空間。
6. 當Server進程處理完讀出的訊息後,然後需要將處理的結果存放在一塊使用者態空間的記憶體中,希望將其返回給Client進程
7. 切換到核心態
8. Binder driver讀Server進程的使用者態空間(0~3G)的處理結果
4. Binder driver將讀出的內容複寫到Client進程的Binder buffer中
5. 然後,當Client進程執行時,就能夠在其使用者態空間(0~3G)讀傳過來的內容了。因為Binder Buffer也被映射到了Client進程的使用者態空間。
這就是Binder driver如何?處理序間通訊的。
至於其中的細節,比如安全機制的實現,就不多說了。感興趣的請參考老羅的鴻篇巨著《Android系統原始碼情景分析》,裡邊有兩百餘頁的篇幅講Binder系統。不過,要看懂這些內容,需要有較深的Linux Kernel知識。