binder執行機制--從應用到原理再到實踐
之前網上看的 binder 相關的文章,總是有的地方沒說清楚,本文目的是以最少的文字說執行機制清楚。
應用
先把程式碼下載下來,跑一跑,看看現場,瞭解一下binder如何使用。
原理
在弄清楚原理先,先了解一下binder由來,在瞭解binder執行機制,最後分析程式碼。
由來
我們知道,在Android系統裡,底層是linux作業系統,而linux作業系統本身是提供了很對跨程序的通訊方式,比如:管道,system V IPC,socket等。這些方法都需要兩次拷貝,當進行大量的程序間通訊時,會影響系統效率。故Android的設計者就想到使用 binder 機制實現程序間的通訊,且是一次拷貝。
如何實現一次拷貝
【前提知識】要想弄明白為什麼可以一次拷貝,就需要先知道:
(1)linux系統分核心空間和使用者空間,每個程序都有一個4G大小的虛擬地址空間,在這個4G大小的虛擬地址空間中,前0~3G為使用者空間,每個程序的使用者空間之間是相互獨立的,互不相干。而3G~4G為核心空間,因為每個程序都可以從使用者態切換到核心態,因此,核心空間(準確的說核心絕大部分空間,核心棧不能共享)對於所有程序來說,可以說是共享的。程序A拷貝資料到程序B的步驟為:先將程序A的資料拷貝到核心空間,再從核心空間拷貝資料到程序B中,所以需要兩次拷貝。
(2)理解什麼是使用者態,什麼是核心態。程序在建立的時候運用在使用者空間,這個時候叫使用者態,當程序呼叫系統呼叫
(3)需要先了解linux的MMU(記憶體管理單元),其作用是將虛擬地址對映到實體地址。我們知道linux系統中,程式執行時使用的是虛擬地址,這樣在真正進行讀寫時,需要將虛擬地址對映到實體地址。這裡需要說明,程序的使用者空間和核心空間是針對虛擬地址而言,而不是實體地址。
下圖說明兩次拷貝的過程:
從上圖可以看到,經過了兩次拷貝,那這個過程可不可以優化呢,即實現一次拷貝,見下圖:
由上圖可知,程序B直接把地址對映到核心空間(這裡應該理解為,程序B的“接受資料”地址和核心快取區地址 對映到同一個記憶體地址中),這樣就可以實現一次拷貝。
binder執行機制
先下結論,binder執行機制如下圖:
說明1:Client程序、Server程序 & Service Manager 程序之間的互動 都必須通過Binder驅動(使用 open 和 ioctl檔案操作函式),而非直接互動。
原因:
(1)Client程序、Server程序 & Service Manager程序屬於程序空間的使用者空間,不可進行程序間互動
(2)Binder驅動 屬於 程序空間的 核心空間,可進行程序間 & 程序內互動
虛線表示:不可以直接互動。
說明2: Binder驅動 & Service Manager程序 屬於 Android基礎架構(即系統已經實現好了);而Client 程序 和 Server 程序 屬於Android應用層(需要開發者自己實現)。所以,在進行跨程序通訊時,開發者只需自定義Client & Server 程序 並 顯式使用上圖的3個步驟,最終藉助 Android的基本架構功能就可完成程序間通訊。
說明3:Binder請求的執行緒管理
Server程序會建立很多執行緒來處理Binder請求。
Binder模型的執行緒管理,採用Binder驅動的執行緒池,並由Binder驅動自身進行管理,而不是由Server程序來管理的。
一個程序的Binder執行緒數預設最大是16,超過的請求會被阻塞,等待空閒的Binder執行緒。所以,在程序間通訊時處理併發問題時,如使用ContentProvider時,它的CRUD(建立、檢索、更新和刪除)方法只能同時有16個執行緒同時工作。
程式碼分析
binder驅動的原始碼在linux核心(版本為4.4.52)中的 drivers/android/binder.c
// 驅動入口函式
device_initcall(binder_init);
驅動入口函式為 device_initcall(),而不是 module_init(),這樣做的目的是,不想成為 支援動態編譯。
//驅動註冊
static struct miscdevice binder_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "binder",
.fops = &binder_fops // Binder驅動支援的檔案操作
};
static int __init binder_init(void)
{
....
ret = misc_register(&binder_miscdev); // 驅動註冊
....
}
misc device 是特殊的字元裝置,也稱為雜項裝置。
// 填寫 file_operation 結構體
static const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.compat_ioctl = binder_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};
這裡比較重要的函式有:binder_open,binder_mmap,binder_ioctl。
實踐
經過上面的分析,對 binder 有大致的瞭解後,知道 binder驅動 執行在linux系統中,那麼可不可以將binder移植到ubuntu下,用insmod 命令載入到核心中,在使用者空間中建立兩個兩個程序和一個server,來模擬binder執行機制呢?
參考
本文主要參考:
https://www.jianshu.com/p/4ee3fd07da14,淺顯易懂,過於囉嗦,給的例項只有部分原始碼,且其中說 Service Manager 屬於使用者空間,經過檢視Android原始碼,確實是在使用者空間,(老羅的那種圖不對,說Service Manager 是核心空間)
https://blog.csdn.net/u011240877/article/details/72801425,上一個的程式碼分析中,對函式的解析比較少,這個對主要函式的解析很詳細。可惜這個沒有例項分析。
最完善的:https://blog.csdn.net/universus/article/details/6211589