Android中的Binder機制是Android系統中最核心和最基礎的進程間通訊機制。
1 什么是進程間通訊機制(IPC)?
眾所周知,Android系統基于Linux開發,Linux系統里面本來就有進程間通訊機制。
1.1 Linux的IPC(Inter-Process Communication)概覽
它是不同進程之間交換數據和消息的一種手段。
但是進程之間默認地址空間是隔離的,有什么解決方案去做到不同進程之間交換信息嗎?答案是當然有。
下圖整理自AI。
類型 | 示例 | 特點說明 |
---|---|---|
文件系統相關 | 管道、命名管道(FIFO) | 簡單,適合父子進程或有命名路徑的通信 |
內存相關 | 共享內存 | 高效,速度快,但需自行同步(如用信號量) |
消息傳遞 | 消息隊列、信號、信號量 | 安全、結構化,但效率略低 |
套接字通信 | 本地 Socket、網絡 Socket | 靈活,可跨主機,適用于客戶端-服務器架構 |
高級通信機制 | DBus、ZeroMQ、gRPC、Binder等 | 抽象層高,適合復雜或跨語言系統 |
對于這個列表,我們重點是需要關注這些方案的原理是什么。只有知道原理,我們才能解開現象背后的本質,對于很多技術難題,我們才能有思路舉一反三。
1.2 文件系統相關(管道、命名管道 FIFO)
這類IPC本質上是一個內核緩沖區,讀寫操作通過文件描述符來跟緩沖區進行交互。
寫端進程通過write寫入緩沖區,讀端進程通過read讀取數據。
這種屬于阻塞式通訊,讀和寫都需要等對方準備好,系統通過文件描述符來判斷是否可以進行讀和寫。
1.3 內存相關(共享內存)
這類IPC,在不同進程的虛擬地址空間通過映射的方式,比如mmap來映射到一段共同的物理內存頁來實現“共享”。
進程通過指針直接訪問內存中的數據結果,完全繞過了內核的讀寫接口,所以速度極快。俗稱“零拷貝”。這種機制是沒有內建鎖的,進程需要通過鎖機制等保證并發安全。
1.4 消息傳遞類(消息隊列、信號、信號量)
消息隊列:內核維護一個消息隊列結構體,發送進程通過系統調用把消息copy到內核隊列中(第一次拷貝)。接收方從隊列中獲取信息(第二次拷貝)。
信號:信號是系統內核或者進程向另一個進程發送的異步通知,用來通知某事件的發生。這是一種輕量級的IPC方式。
核心原理是內核使用位圖或者信號隊列標記進程收到的信號,調度器在合適的時候調用信號處理函數。
信號量:是一種同步原語。本質上是內核中的一個整數值結構 + 等待隊列,用于資源訪問同步。
P/V 操作中,P(等待)將信號量值減一,若 < 0 進程阻塞;V(釋放)加一,并喚醒等待隊列中的進程。
1.5 套接字通信(本地 / 網絡 Socket)
Linux 為每個 Socket 創建一個 socket 緩沖區結構(sk_buff),內核中維護雙向隊列。
本地 Socket 使用 UNIX Domain 協議族,數據在內核中傳輸,無需網絡協議棧處理。
網絡 Socket 走 TCP/IP 協議棧,數據被封包、路由、擁塞控制,完整鏈路層處理。
2 Android特有的IPC:Binder
2.1 Binder機制的有什么用?
Binder 是 Android 提供的一種高性能的 IPC 機制,用于實現:
- 跨進程方法調用(如 A 應用調用系統服務中的方法)
- 客戶端-服務端架構模型
- 安全的權限校驗
- 引用計數和生命周期管理
2.2 Binder機制為什么快?
可以先看下傳統的消息傳遞類IPC做法。
圖片來源:https://blog.csdn.net/carson_ho/article/details/73560642
而binder通信機制基于內核 Binder 驅動,使用內核內存映射區(Binder Buffer)做 zero-copy 通信。來看下Binder的做法。
圖片來源:https://blog.csdn.net/carson_ho/article/details/73560642
他的通訊流程是:發送進程 ->內核緩沖區(第一次拷貝) ->映射到接收進程。
2.2 Binder機制由什么組成?
Binder 是一個 C/S 模型(客戶端/服務端),主要組成部分如下:
-
ServiceManager
系統服務注冊與發現的“電話簿”,所有服務通過它注冊和查找。 -
Binder 驅動(Binder Driver)
位于內核空間(/dev/binder),實現核心的 IPC 邏輯,如數據傳輸、線程調度、引用計數等。 -
Binder 本地端(Client 端)
通過代理類(Proxy)與服務端通信,通常由 AIDL 自動生成。 -
Binder 服務端(Server 端)
實現具體的業務邏輯,繼承 Binder 類并重寫方法。 -
Binder線程池(Binder線程)
服務端進程中專門處理來自 Binder 驅動的調用請求的線程(由 BinderThreadPool 管理)。
下面是一個App調用系統AMS的一個例子:
3 常見問題
3.1 如果超過16個進程同時在使用AIDL進行通訊,會發生什么?
首先,16 并不是硬性限制,“16個進程”來源于 Android Binder 線程池默認上限,它是一個服務端并發處理能力的限制,而不是進程數量的限制。
Android 中,服務端 Binder 對象默認最多有 16 個 Binder 線程(可通過 android.os.Binder.setThreadPoolMaxThreadCount(int) 設置)。
而這個限制影響的是,服務端一次性能并發處理的 Binder 請求線程數。
并不是說客戶端數量最多 16 個,而是服務端的同時“處理線程”是 16。
那么如果超過 16 個進程同時通過 AIDL 訪問一個服務,會發生什么呢?
-
請求不會失敗,但需要排隊等待,如果服務端的 16 個 Binder 線程都在處理請求,第 17 個及之后的請求將進入等待隊列。
-
客戶端 transact() 系統調用會阻塞,直到服務端有空閑線程。
-
服務響應變慢(可觀的延遲)。隨著并發請求數增加,響應時間線性上升,尤其是服務端執行耗時任務時。比如,圖像處理、數據庫查詢等在主線程處理會導致嚴重卡頓。
-
線程池飽和后,部分請求可能被拒絕(極端情況)。如果服務端主動設置了最大等待隊列長度(如基于 ThreadPoolExecutor),也可能在高負載時拒絕新請求(拋出 RejectedExecutionException)。但這取決于服務的實現方式,Binder 驅動本身仍是隊列化的。
-
主線程阻塞風險,如果服務端是 onTransact() 或處理邏輯運行在主線程上,那么并發調用容易造成 ANR(應用無響應)。Google 明確建議服務端操作應放到 HandlerThread / BinderThreadPool / JobScheduler 等后臺線程。
后續繼續補充。。。