1. 程式人生 > >binder執行機制--從應用到原理再到實踐

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)理解什麼是使用者態,什麼是核心態。程序在建立的時候運用在使用者空間,這個時候叫使用者態,當程序呼叫系統呼叫

(open,read、close等)時,進入核心空間,此時叫核心態。因此每個程序都有兩個棧,即使用者棧和核心棧。

(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