談談 Binder 機制--標準答案

binder學習指南
http://weishu.me/2016/01/12/binder-index-for-newer/
carson_ho 的講解
https://blog.csdn.net/carson_ho/article/details/73560642
mmp()
http://www.lxweimin.com/p/719fc4758813
android 多進程,用來學習 AIDL
https://segmentfault.com/a/1190000022243232

1.什么是Binder
Binder是一種進程間通信(IPC)的機制。

在Android系統的Binder機制中,由4個組件組成,分別是Client、Server、Service Manager和Binder驅動,其中Client、Server和Service Manager運行在用戶空間,Binder驅動程序運行在內核空間。
Bind驅動有三個核心方法: binder_open, binder_mmap, binder_ioctl
binder_open 主要負責在內核空間中注冊進程信息 binder_proc, 后續操作以此為依據
binder_mmap 主要負責進行內存映射, 起始為一頁物理內存4kB,最大為 4M, 并將虛擬地址保存在 binder_proc->buffer中, 再計算出應用程序的虛擬內存, 后續數據讀取涉及到缺頁中斷
binder_ioctl 主要負責讀寫等文件操作

其中,核心組件便是Binder驅動。Service Manager提供了輔助管理的功能,Client和Server正是在Binder驅動和Service Manager提供的基礎設施上,進行Client-Server之間的通信。Service Manager和Binder驅動已經在Android平臺中實現好,開發者只要按照規范實現自己的Client和Server組件就可以了。

2.跨進程通信的方式

經典的IPC方式有:

  • 共享內存
  • 管道
  • unix domain socket
    Socket: socket分為ServerSocket(tcp)、DatagramSocket(udp)、LocalServerSocket(unix ipc)

而android特有的方式有:

  • ContentProvider 支持跨進程數據共享
  • Binder機制 使用Binder還可以具體為AIDL和Messenger.

為什么使用binder機制,而不使用操作系統已有的IPC機制?

  1. 從性能的角度。Binder數據拷貝只需要一次,而管道、消息隊列、Socket都需要2次,但共享內存方式一次內存拷貝都不需要;從性能角度看,Binder性能僅次于共享內存。
  2. 從穩定性的角度。Binder是基于C/S架構的。客戶端和服務端相互獨立,穩定性好。
  3. 從安全的角度。傳統Linux IPC的接收方無法獲得對方進程可靠的UID/PID,從而無法鑒別對方身份,而Binder有一套上層的安全機制,可以確保app在沒有權限的情況下無法執行服務。
  4. 最重要的是,公司戰略層面, Linux內核是開源的系統, 所有進行系統調用的系統都必須開發源碼. Google巧妙地將GPL協議控制在內核空間,將用戶空間的協議采用Apache-2.0協議(允許基于Android的開發商不向社區反饋源碼)

實際上android中的IPC并不單一, Zygote進程的IPC采用的是Socket機制,Kill Process采用的signal(信號)機制等等。而Binder更多則用在system_server進程與上層App層的IPC交互。

  1. Binder的一次拷貝是哪次
    binder的一次拷貝發生在客戶端向服務進程發消息時,從用戶空間拷貝消息到內核空間. 對應服務端進程的 proc->buffer中, 這個buffer 和服務端的數據都是虛擬內存, 由 mmp() 返回, 映射了同一個物理地址


4.介紹下AIDL
AIDL是一個生成代碼編譯機制,方便開發者編寫通過Binder進程間通訊的代碼。通過aidl文件定義好接口后,編譯工具會生成一個interface的java類,這個interface包含一個叫Stub的靜態內部抽象類,Stub又包含一個叫Proxy的私有內部靜態類。

簡單地說,AIDL跨進程通信就是Server和Client約定好方法號和參數,通過binder驅動將方法號和參數從一端傳遞到另一端的過程。

Service和Activity跨進程通信使用 binder,Activity和Activity 跨進程通信可以使用startActivity和onActivityResult。

使用方式:
在Server進程實例化一個Stub, 在onBind回調中返回。在Client進程中bindService,通過ServiceConnection拿到一個IBinder,通過Stub.asInterface(binder)實例化一個interface的代理Proxy,Client就獲得了一個interface的實例。Client通過調用proxy的方法,向binder.transact方法傳入方法序號,data parcel,reply parcel和是否oneway的flag,Binder驅動將這些參數傳遞至Server的Stub實例,在Stub的onTransact中解組parcel,通過方法序號來執行對應的方法。

更復雜一些的,通過AIDL還可以讓Client向Server注冊回調。首先用aidl定義一個單獨的回調接口,使用AIDL生成的接口都繼承了IBinder接口,都可以被寫入parcel。Client實例化這個回調接口的Stub,在ServiceConnection獲得注冊接口的proxy后,將回調傳給注冊接口,把回調binder寫入注冊接口的data parcel中,通過Binder驅動傳遞至Server,Server再拿到這個Client的binder后, 將它保存至一個RemoteCallbackList,在合適的時機調用這個回調,再進行一次從Server到Client的進程間通信。

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

推薦閱讀更多精彩內容