為什麼 Android 要採用 Binder 作為 IPC 機制?

Android 另起爐灶開發了 Binder 驅動,而沒有採用已有的方案,而 D-Bus 這樣的方案也可以實現 Binder 的功能,是出於什麼原因和什麼考慮?安全性?性能?


為什麼Android要採用Binder作為IPC機制?

在開始回答 前,先簡單概括性地說說Linux現有的所有進程間IPC方式:

1. 管道:在創建時分配一個page大小的內存,緩存區大小比較有限;

2. 消息隊列:信息複製兩次,額外的CPU消耗;不合適頻繁或信息量大的通信;

3. 共享內存:無須複製,共享緩衝區直接付附加到進程虛擬地址空間,速度快;但進程間的同步問題操作系統無法實現,必須各進程利用同步工具解決;

4. 套接字:作為更通用的介面,傳輸效率低,主要用於不通機器或跨網路的通信;

5. 信號量:常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。

6. 信號: 不適用於信息交換,更適用於進程中斷控制,比如非法內存訪問,殺死某個進程等;

Android的內核也是基於Linux內核,為何不直接採用Linux現有的進程IPC方案呢,難道Linux社區那麼多優秀人員都沒有考慮到有Binder這樣一個更優秀的方案,是google太過於牛B嗎?事實是真相併非如此,請細細往下看,您就明白了。

-------------------------------------------------------------------------------------------------------------------------------------------

接下來正面回答這個問題,從5個角度來展開對Binder的分析:

(1)從性能的角度

數據拷貝次數:Binder數據拷貝只需要一次,而管道、消息隊列、Socket都需要2次,但共享內存方式一次內存拷貝都不需要;從性能角度看,Binder性能僅次於共享內存。

(2)從穩定性的角度

Binder是基於C/S架構的,簡單解釋下C/S架構,是指客戶端(Client)和服務端(Server)組成的架構,Client端有什麼需求,直接發送給Server端去完成,架構清晰明朗,Server端與Client端相對獨立,穩定性較好;而共享內存實現方式複雜,沒有客戶與服務端之別, 需要充分考慮到訪問臨界資源的並發同步問題,否則可能會出現死鎖等問題;從這穩定性角度看,Binder架構優越於共享內存。

僅僅從以上兩點,各有優劣,還不足以支撐google去採用binder的IPC機制,那麼更重要的原因是:

(3)從安全的角度

傳統Linux IPC的接收方無法獲得對方進程可靠的UID/PID,從而無法鑒別對方身份;而Android作為一個開放的開源體系,擁有非常多的開發平台,App來源甚廣,因此手機的安全顯得額外重要;對於普通用戶,絕不希望從App商店下載偷窺隱射數據、後台造成手機耗電等等問題,傳統Linux IPC無任何保護措施,完全由上層協議來確保。

Android為每個安裝好的應用程序分配了自己的UID,故進程的UID是鑒別進程身份的重要標誌,前面提到C/S架構,Android系統中對外只暴露Client端,Client端將任務發送給Server端,Server端會根據許可權控制策略,判斷UID/PID是否滿足訪問許可權,目前許可權控制很多時候是通過彈出許可權詢問對話框,讓用戶選擇是否運行。Android 6.0,也稱為Android M,在6.0之前的系統是在App第一次安裝時,會將整個App所涉及的所有許可權一次詢問,只要留意看會發現很多App根本用不上通信錄和簡訊,但在這一次性許可權許可權時會包含進去,讓用戶拒絕不得,因為拒絕後App無法正常使用,而一旦授權後,應用便可以胡作非為。

針對這個問題,google在Android M做了調整,不再是安裝時一併詢問所有許可權,而是在App運行過程中,需要哪個許可權再彈框詢問用戶是否給相應的許可權,對許可權做了更細地控制,讓用戶有了更多的可控性,但同時也帶來了另一個用戶詬病的地方,那也就是許可權詢問的彈框的次數大幅度增多。對於Android M平台上,有些App開發者可能會寫出讓手機異常頻繁彈框的App,企圖直到用戶授權為止,這對用戶來說是不能忍的,用戶最後吐槽的可不光是App,還有Android系統以及手機廠商,有些用戶可能就跳果粉了,這還需要廣大Android開發者以及手機廠商共同努力,共同打造安全與體驗俱佳的Android手機。

Android中許可權控制策略有SELinux等多方面手段,下面列舉從Binder的一個角度的許可權控制:

Android源碼的Binder許可權是如何控制? -Gityuan的回答

傳統IPC只能由用戶在數據包里填入UID/PID;另外,可靠的身份標記只有由IPC機制本身在內核中添加。其次傳統IPC訪問接入點是開放的,無法建立私有通道。從安全形度,Binder的安全性更高。

說到這,可能有人要反駁,Android就算用了Binder架構,而現如今Android手機的各種流氓軟體,不就是干著這種偷窺隱射,後台偷偷跑流量的事嗎?沒錯,確實存在,但這不能說Binder的安全性不好,因為Android系統仍然是掌握主控權,可以控制這類App的流氓行為,只是對於該採用何種策略來控制,在這方面android的確存在很多有待進步的空間,這也是google以及各大手機廠商一直努力改善的地方之一。在Android 6.0,google對於app的許可權問題作為較多的努力,大大收緊的應用許可權;另外,在Google舉辦的Android Bootcamp 2016大會中,google也表示在Android 7.0 (也叫Android N)的許可權隱私方面會進一步加強加固,比如SELinux,Memory safe language(還在research中)等等,在今年的5月18日至5月20日,google將推出Android N。

話題扯遠了,繼續說Binder。

(4)從語言層面的角度

大家多知道Linux是基於C語言(面向過程的語言),而Android是基於Java語言(面向對象的語句),而對於Binder恰恰也符合面向對象的思想,將進程間通信轉化為通過對某個Binder對象的引用調用該對象的方法,而其獨特之處在於Binder對象是一個可以跨進程引用的對象,它的實體位於一個進程中,而它的引用卻遍佈於系統的各個進程之中。可以從一個進程傳給其它進程,讓大家都能訪問同一Server,就像將一個對象或引用賦值給另一個引用一樣。Binder模糊了進程邊界,淡化了進程間通信過程,整個系統彷彿運行於同一個面向對象的程序之中。從語言層面,Binder更適合基於面向對象語言的Android系統,對於Linux系統可能會有點「水土不服」。

另外,Binder是為Android這類系統而生,而並非Linux社區沒有想到Binder IPC機制的存在,對於Linux社區的廣大開發人員,我還是表示深深佩服,讓世界有了如此精湛而美妙的開源系統。也並非Linux現有的IPC機制不夠好,相反地,經過這麼多優秀工程師的不斷打磨,依然非常優秀,每種Linux的IPC機制都有存在的價值,同時在Android系統中也依然採用了大量Linux現有的IPC機制,根據每類IPC的原理特性,因時制宜,不同場景特性往往會採用其下最適宜的。比如在Android OS中的Zygote進程的IPC採用的是Socket(套接字)機制,Android中的Kill Process採用的signal(信號)機制等等。而Binder更多則用在system_server進程與上層App層的IPC交互

(5) 從公司戰略的角度

總所周知,Linux內核是開源的系統,所開放源代碼許可協議GPL保護,該協議具有「病毒式感染」的能力,怎麼理解這句話呢?受GPL保護的Linux Kernel是運行在內核空間,對於上層的任何類庫、服務、應用等運行在用戶空間,一旦進行SysCall(系統調用),調用到底層Kernel,那麼也必須遵循GPL協議。

而Android 之父 Andy Rubin對於GPL顯然是不能接受的,為此,Google巧妙地將GPL協議控制在內核空間,將用戶空間的協議採用Apache-2.0協議(允許基於Android的開發商不向社區反饋源碼),同時在GPL協議與Apache-2.0之間的Lib庫中採用BSD證授權方法,有效隔斷了GPL的傳染性,仍有較大爭議,但至少目前緩解Android,讓GPL止步於內核空間,這是Google在GPL Linux下 開源與商業化共存的一個成功典範。

有了這些鋪墊,我們再說說Binder的今世前緣

Binder是基於開源的 OpenBinder實現的,OpenBinder是一個開源的系統IPC機制,最初是由 Be Inc. 開發,接著由Palm, Inc.公司負責開發,現在OpenBinder的作者在Google工作,既然作者在Google公司,在用戶空間採用Binder 作為核心的IPC機制,再用Apache-2.0協議保護,自然而然是沒什麼問題,減少法律風險,以及對開發成本也大有裨益的,那麼從公司戰略角度,Binder也是不錯的選擇。

另外,再說一點關於OpenBinder,在2015年OpenBinder以及合入到Linux Kernel主線 3.19版本,這也算是Google對Linux的一點回饋吧。

綜合上述5點,可知Binder是Android系統上層進程間通信的不二選擇。

------------------------------------------------------------------------------------------------------------------------------------------

接著,回答樓主提到的D-Bus

也採用C/S架構的IPC機制,D-Bus是在用戶空間實現的方法,效率低,消息拷貝次數和上下文切換次數都明顯多過於Binder。針對D-Bus這些缺陷,於是就產生了kdbus,這是D-Bus在內核實現版,效率得到提升,與Binder一樣在內核作為字元設計,通過open()打開設備,mmap()映射內存。

(1)kdbus在進程間通信過程,Client端將消息在內存的消息隊列,可以存儲大量的消息,Server端不斷從消息隊里中取消息,大小隻受限內存;

(2)Binder的機制是每次通信,會通信的進程或線程中的todo隊里中增加binder事務,並且每個進程所允許Binder線程數,google提供的默認最大線程數為16個,受限於CPU,由於線程數太多,增加系統負載,並且每個進程默認分配的(1M-8K)大小的內存。

而kdbus對於內存消耗較大,同時也適合傳輸大量數據和大量消息的系統。Binder對CPU和內存的需求比較低,效率比較高,從而進一步說明Binder適合於移動系統Android,但是,也有一定缺點,就是不同利用Binder輸出大數據,比如利用Binder傳輸幾M大小的圖片,便會出現異常,雖然有廠商會增加Binder內存,但是也不可能比系統默認內存大很多,否則整個系統的可用內存大幅度降低。

最後,簡單講講Android Binder架構

Binder在Android系統中江湖地位非常之高。在Zygote孵化出system_server進程後,在system_server進程中出初始化支持整個Android framework的各種各樣的Service,而這些Service從大的方向來劃分,分為Java層Framework和Native Framework層(C++)的Service,幾乎都是基於BInder IPC機制。

  1. Java framework:作為Server端繼承(或間接繼承)於Binder類,Client端繼承(或間接繼承)於BinderProxy類。例如 ActivityManagerService(用於控制Activity、Service、進程等) 這個服務作為Server端,間接繼承Binder類,而相應的ActivityManager作為Client端,間接繼承於BinderProxy類。 當然還有PackageManagerService、WindowManagerService等等很多系統服務都是採用C/S架構;

  2. Native Framework層:這是C++層,作為Server端繼承(或間接繼承)於BBinder類,Client端繼承(或間接繼承)於BpBinder。例如MediaPlayService(用於多媒體相關)作為Server端,繼承於BBinder類,而相應的MediaPlay作為Client端,間接繼承於BpBinder類。

總之,一句話"無Binder不Android"。

本來想從Binder源碼技術的角度,分析Binder如何做到的,發現不知不覺就寫了這麼多,對於實現原理有興趣,查看我的個人博客。通過Google搜索關鍵字 「Binder系列」,第一個出現的便是我的博客 Yuanhh.com,上一張 Google搜索結果的截圖:

為了便於傳播與記憶,剛剛申請了新域名gityuan(與我的微博、知乎ID同名),個人博客由http://yuanhh.com遷移到新域名 http://Gityuan.com,由於不擅長SEO,網站的google權重降低,更新時間 2016.03.27。

有網友建議,放上Binder系列的連接:Binder系列—開篇。 更新時間2016.04.09

最後的最後:

朋友推薦來知乎這邊回答網友的問題,漲漲人氣,我也是拼了,第一次這麼長篇大論的回答知乎的問題,感覺沒說清楚,修訂了一遍又一遍,如果大家覺得我回答得還行,還請大家隨手 點贊、關注、收藏,如果覺得說得不好的,還往評論指正。 若能得到大家的肯定,那也不枉費花時間敲打這麼多文字,在這裡 Gityuan先謝謝大家。

==========&> 我的微博:Gityuan

==========&>微信公眾號: Gityuan

之前一直在埋頭做技術,最近剛剛開通微信、微博,後面會有更多的乾貨分享,歡迎大家關注,謝謝!!

------------------------------------------------------------------------------------------------------------------------------------------

關於我是如何學習Android,可以查看我的另一篇知乎文章: 如何自學Android?


首先澄清一點,Android沒有另起爐灶,Binder機制源於OpenBinder.

至於D-Bus與OpenBinder孰好孰壞的問題,我覺得現在的D-bus也不錯;為什麼用Binder是因為Android剛剛誕生的時候,D-bus並不成熟;現在也有人想用kdbus替換binder,個人覺得已無力回天。

------------------------------------------------------------------------------------------------

Binder與傳統IPC機制

那麼,與Linux上傳統的IPC機制,比如System V,Socket相比,Binder好在哪呢?個人認為有如下幾點:

1. 性能;Binder傳輸只需要一次copy;socket兩次,別小看這一倍帶來的差距。對於移動設備,性能一直是個大問題;想一下Android繪製界面的時候都需要與WindowManager進行跨進程通信,如果這裡效率不高,那豈不是卡死?

2. 安全性;Binder機制對於通信雙方的身份是內核進行校檢支持的;socket方式只需要知道地址都可以連接;安全機制需要上層協議架設。

3. 易用性;共享內存不需要copy性能夠高,可是使用複雜;B/S模式的通信,如果管道/消息隊列還得進行包裝;另外,Binder使用面向對象的方式設計,進行一次遠程過程調用就好像直接調用本地對象一樣,異常方便。

另外,引用 Brian Swetland 大神的回答:

Q: Why does [Binder] need to be done in the kernel? Couldn"t any of the current Linux IPC mechanisms be re-used to accomplish this?

A: Brian Swetland answers here:

I believe the two notable properties of the binder that are not present in existing IPC mechanisms in the kernel (that I"m aware of) are:

  • avoiding copies by having the kernel copy from the writer into a ring buffer in the reader"s address space directly (allocating space if necessary)
  • managing the lifespan of proxied remoted userspace objects that can be shared and passed between processes (upon which the userspace binder library builds its remote reference counting model)

大意就是:

1. 避免內核空間到數據接受端的直接的數據拷貝;數據接受端接收數據的時候,由於數據大小不確定,要麼分配一個很大的空間裝數據,要麼動態擴容;兩種方式都有問題;Binder使用mmap直接把接受端的內存映射到內存空間,避免了數據的直接拷貝;另外通過data_buffer等方式讓數據僅包含定長的消息頭,解決了接受端內存分配的問題。

2. 需要管理跨進程傳遞的代理對象的生命周期;這一點其他機制無法完成;Binder驅動通過引用計數技術解決這個問題。

-------------------------------------------------------------------------------------------------

Binder vs kdbus

這個問題已經有人解答了;甚至也有人想用kdbus替代binder;原文鏈接:kdbus details

Binder is bound to the CPU, D-Bus (and hence kdbus), is bound to RAM.

簡單解釋如下:

Binder是為微內核類設備創建的。功能有限,缺乏靈活性,但開銷低、速度快。Binder保證CPU時間片從調用進程傳給被調用進程的線程,工作完成後再返回給調用進程。其中幾乎不需要進程調度,非常適合RAM和CPU配置很低的設備。

D-Bus是創建-存儲-轉發,構建回復,再創建-存儲-轉發的消息模型。比bind複雜得多,也更靈活、通用、網路透明、易於管理,且可以容易的管理不被信任的對端參加的通信(不要讓binder面對這種情況,否則...)D-Bus可以處理大塊的數據,在kdbus實現里可以將GB級別的數據傳送給匯流排上的每個連接。從CPU的角度看,D-Bus沒有binder有效率,但是通用性更好。

參考:Android Binder


前面人都說了Binder的優點,我來講故事

1. 當年Andy Rubin有個公司 Palm 做掌上設備的 就是當年那種PDA 有個系統叫PalmOS 後來palm被收購了以後 Andy Rubin 創立了Android

2. Palm收購過一個公司叫 Be 裡面有個移動系統 叫 BeOS 進程通信自己學了個實現 叫Binder 由一個叫 Dianne Hackbod的人開發並維護 後來Binder 也被用到了 PalmOS里

3. Android創立了以後 Andy從Palm帶走了一大批人,其中就有Dianne。Dianne成為安卓系統總架構師。

如果你是她,你會選擇用a.Linux已有的進程通信手段嗎? 不會 要不當年也不會搞個新東西出來

b. 重寫一個新東西 也不會 binder反正是自己寫的開源庫

c. 用binder 已經被兩個公司用過 而且是自己寫的 可靠放心

我是她我就選C

你可以看到 如果當年Dianne沒有加入Be 或者Be沒有被收購 ,又或者Dianne沒有和Andy加入Android 那Android也不一定會用binder。


概括說下我個人所理解的 Bidner和 傳統IPC的不同處。

1. Bidner 是有統一中心化管理的。

Service Manager統一管理所有 public的Binder, 你可以註冊發布Service供其他進程使用。

2. Bidner的安全性更高

根據client端的 uid,做安全訪問控制;

selinux策略加強Binder的安全

3. 高效率

減少了拷貝

4. Binder 是 一對多的, 一個service端同時服務多個client

一個Binder可以服務多個client, 它是 android framework服務化的基礎


關於為什麼要採用Binder這套IPC,很多答主已經大概回答了,這裡歪個題,必須推薦一下。

Binder的文章有很多,但多數是從源碼角度去分析。源碼分析有一個問題就是,細節太多了,特別初接觸Binder的話,是很難聚焦到某個具體問題上的,導致很難總結出Binder這套IPC的設計理念和核心結構。這裡強烈推薦一篇文章,個人認為在眾多介紹Binder中,最一針見血,且簡單明了:

Android Bander設計與實現 - 設計篇

侵刪~


1.首先Binder採用了linux的優秀的內存管理技術,在通過內科空間傳遞數據時,能保證數據的正確性。2.使用用戶空間無法訪問的內存空間來交換數據,保證了IPC的安全性

具體看我的博客:

Android為什麼使用Binder-android學習之旅(101)


看了半個月Binder driver談談體會

效率上只有在做Binder transaction的時候才會有一次用戶空間的數據拷貝,所以效率上比較高。

其次安全性能夠知道別的每次傳輸的源和目的地,基於每次打開設備文件時在內核空間創建的Binder proc,收到數據的也能夠判別信息是那個進程丟給你的。

其實仔細觀察Binder通訊過程中的數據打包流程跟各種tcpip這種很相似。

在Android中使用Binder最好還是使用現成的ipcthreadstate,當請求返回時會自動幫你去free driver中申請的buffer,自己去ioctl使用Binder的話容易忘記,有Binder leak的風險


推薦閱讀:

Linux 用戶一打開終端就 sudo su 算是壞習慣嗎?
使用命令行真的比使用可視化界面好嗎?
正在學習嵌入式linux開發,求推薦詳細介紹makefile和鏈接腳本語法相關的書籍或者資料。?
怎麼看待 Linus 和 Richard Stallman 對 C++ 的態度?
Linux是否有類似於IIS統一管理網站的工具?

TAG:操作系統 | Linux | Android開發 | Android |