《深入理解Android內(nèi)核設(shè)計(jì)思想》筆記之Binder

本篇文章是《深入理解Android內(nèi)核設(shè)計(jì)思想》的讀書筆記,可能就沒書本來得詳細(xì)。

image.png

Binder分為四個(gè)部分

  • Binder驅(qū)動(dòng):運(yùn)行于內(nèi)核態(tài),可以提供open(),ioctl(),mmap()等常用的文件操作
  • ServiceManager:保存所有Binder Server。
  • Binder Client
  • Binder Service

一.Binder驅(qū)動(dòng)

  • 打開Binder驅(qū)動(dòng)-binder-open,在里面創(chuàng)建了一個(gè)它自己的binder_proc實(shí)體
    binder_proc是Binder驅(qū)動(dòng)為應(yīng)用進(jìn)程分配的一個(gè)數(shù)據(jù)結(jié)構(gòu),用于存儲(chǔ)和該進(jìn)程有關(guān)的所有信息,如內(nèi)存分配線程管理等。

binder-open{
生成binder_proc對(duì)象
binder_proc初始化
把它添加到Binder的全局管理中(資源互斥添加)
}

到這里,Binder驅(qū)動(dòng)已經(jīng)為用戶創(chuàng)建了一個(gè)它自己的binder_proc實(shí)體,之后用戶對(duì)Binder的設(shè)備操作將以這個(gè)對(duì)象為基礎(chǔ)。

  • binder_mmap,最多支持4MB的操作
    對(duì)于應(yīng)用程序而言,調(diào)用mmap()方法可以把設(shè)備指定的內(nèi)存塊映射到應(yīng)用程序的內(nèi)存空間中
    而對(duì)于Binder驅(qū)動(dòng)來說,上層用戶調(diào)用的mmap()最終就對(duì)應(yīng)了binder_mmap()

    • 對(duì)于應(yīng)用程序,它通過mmap()返回值得到一個(gè)虛擬內(nèi)存地址,之后通過虛擬內(nèi)存轉(zhuǎn)換(分段分頁)最終指向物理地址某個(gè)地方

    • 對(duì)于Binder驅(qū)動(dòng)而言,有個(gè)指針binder_proc->buffer指向某個(gè)虛擬內(nèi)存地址,與應(yīng)用程序指向一樣,兩者共用內(nèi)存

  • binder_ioctl
    可以替代read,write。對(duì)應(yīng)命令 INDER_WRITE_READ,承擔(dān)Binder驅(qū)動(dòng)的大部分業(yè)務(wù)

  • 小結(jié)
    Binder驅(qū)動(dòng)并沒有脫離Linux的典型驅(qū)動(dòng)模型,它提供了多個(gè)文件操作接口mmap,ioctl。其binder_ioctl實(shí)現(xiàn)了應(yīng)用程序與Binder驅(qū)動(dòng)之間的命令交互,可以說承載了Binder驅(qū)動(dòng)中的大部分業(yè)務(wù)。

二.ServiceManager - Binder Service

  • ServiceManager啟動(dòng)
    在init.rc時(shí)啟動(dòng)(參考系統(tǒng)啟動(dòng)章節(jié)),如果它發(fā)生重啟,其他系統(tǒng)服務(wù)zygote,media,surfaceflinger,drm也會(huì)被重新加載

  • ServiceManager構(gòu)建

int main{
binder_open打開Binder設(shè)備,做好初始化(各種內(nèi)存地址)
binder_become_context_manager將自己設(shè)為Binder大管家,整個(gè)Android程序中只允許一個(gè)ServiceManager存在
binder_loop進(jìn)入主循環(huán)
}

* binder_open初始化
>    * binder_state 記錄SM中有關(guān)于Binder的所有信息
     * mmap由Binder驅(qū)動(dòng)決定被映射到進(jìn)程空間中的內(nèi)存起始地址
     * 映射區(qū)大小為128K,只讀
     * 從文件的起始地址開始映射
* binder_become_context_manager初始化
>    * 調(diào)用到ioctl方法
     * ioctl方法中發(fā)送向Binder Driver發(fā)送 **BINDER_SET_CONTEXT_MGR**的ioctl命令使其成為大管家。(只要向Binder驅(qū)動(dòng)發(fā)送BINDER_SET_CONTEXT_MGR的ioctl命令即可,因?yàn)镾ervicerManager啟動(dòng)的很早,能確保它是系統(tǒng)中第一個(gè)向Binder驅(qū)動(dòng)注冊成管家的程序)
* binder_loop循環(huán)等待客戶端請(qǐng)求
>    * binder_write_read 執(zhí)行**BINDER_WRITE_READ**所需的數(shù)據(jù)格式
     * 開始循環(huán)讀取消息(它的消息是從Binder驅(qū)動(dòng)那里獲取的)
     * 從Binder驅(qū)動(dòng)讀取消息,通過**BINDER_WRITE_READ**命令
     * binder_parse處理消息
     * 不斷循環(huán),而且永遠(yuǎn)不會(huì)主動(dòng)退出,除非出現(xiàn)致命錯(cuò)誤

ServiceManager的功能架構(gòu)比較簡潔,內(nèi)部維護(hù)著一個(gè)svclist列表,用于存儲(chǔ)所有Server(Binder Service)的相關(guān)信息(以svcinfo為數(shù)據(jù)結(jié)構(gòu)),查詢和注冊都是基于這個(gè)表展開

  • 獲取ServiceManager服務(wù)
    準(zhǔn)確說,獲取SM服務(wù)是Binder Client需要做的工作之一。

訪問SM服務(wù)流程

1.打開Binder設(shè)置
2.執(zhí)行mmap內(nèi)存映射
3.通過Binder驅(qū)動(dòng)向SM發(fā)送請(qǐng)求(SM的handle為0)
4.獲取結(jié)果

每個(gè)進(jìn)程只允許打開一次Binder設(shè)備,且只做一次內(nèi)存映射---所有需要使用Binder驅(qū)動(dòng)的線程共享這一資源。

獲取SM服務(wù)流程圖

獲取SM服務(wù)

進(jìn)程中ProcessState和IPCThreadState這兩個(gè)類專用與Binder驅(qū)動(dòng)通信,所以Java層代碼使用Binder驅(qū)動(dòng)實(shí)際上是基于他們來完成的,我們稱為BpBinder。

應(yīng)用程序通過代理ServiceManagerProxy(能夠跨進(jìn)程調(diào)用)來獲取到SM服務(wù),它所能提供的服務(wù)和服務(wù)端的SM必須是一致的,也就是它也要實(shí)現(xiàn)服務(wù)端SM對(duì)應(yīng)的接口。
這里的SM指的是Binder層面的ServiceManager。

對(duì)于獲取SM服務(wù)的代理ServiceManagerProxy,Android系統(tǒng)在ServiceManagerProxy上又加了一層封裝ServiceManager.java

2.1 獲取SM服務(wù)

首先調(diào)用ServiceManager.getService(name)

 public static IBinder getService(String name) {
        try {
            IBinder service = sCache.get(name);
            if (service != null) {
                return service;
            } else {
                return getIServiceManager().getService(name);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "error in getService", e);
        }
        return null;
    }

其中sCache用于記錄getService的歷史查詢結(jié)果,加快查詢速度。如果不在則調(diào)用getIServiceManager().getService(name);
getIServiceManager方法用于獲取一個(gè)IServiceManager對(duì)象,而IServiceManager接口的實(shí)現(xiàn)類則是ServiceManagerProxy

 private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }

        // Find the service manager
        sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
        return sServiceManager;
    }

調(diào)用ServiceManagerNative的方法

static public IServiceManager asInterface(IBinder obj)
    {
        if (obj == null) {
            return null;
        }
        IServiceManager in =
            (IServiceManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }
        
        return new ServiceManagerProxy(obj);
    }

先查詢本地是否已經(jīng)有了IServiceManager,如果沒有則新建一個(gè)。

最終則是調(diào)用到了ServiceManagerProxy.getService

public IBinder getService(String var1) throws RemoteException {
        Parcel var2 = Parcel.obtain();
        Parcel var3 = Parcel.obtain();
        var2.writeInterfaceToken("android.os.IServiceManager");
        var2.writeString(var1);
        this.mRemote.transact(1, var2, var3, 0);
        IBinder var5 = var3.readStrongBinder();
        var3.recycle();
        var2.recycle();
        return var5;
    }

該函數(shù)分為以下3部分

  • 準(zhǔn)備數(shù)據(jù)。也就是通過Parcel打包數(shù)據(jù)
  • IBinder.transact,利用IBinder的transact將請(qǐng)求發(fā)送出去,而不用理會(huì)Binder驅(qū)動(dòng)的open,mmap以及一大堆具體的 Binder協(xié)議中的命令。所以這個(gè)IBinder一定會(huì)在內(nèi)部使用ProcessStateIPCThreadState來與Binder驅(qū)動(dòng)進(jìn)行通訊。
  • 獲取結(jié)果。
    上面transact后,我們就可以直接獲取到結(jié)果了。如果大家用過socket,就知道這是一種阻塞式的函數(shù)調(diào)用。因?yàn)樯婕暗竭M(jìn)程間通信,結(jié)果并不是馬上就能獲取到,所以Binder驅(qū)動(dòng)一定要先將調(diào)用者線程掛起,直到有了結(jié)果才把它喚醒。

那么IBinder內(nèi)部是怎么實(shí)現(xiàn)的呢?

2.2 IBinder內(nèi)部

前面我們的IBinder對(duì)象是通過BinderInternal.getContextObject()獲取到的
而它則是一個(gè)native方法

public static final native IBinder getContextObject();

因?yàn)楹虰inder驅(qū)動(dòng)打交道,最終都得通過JNI調(diào)用本地代碼來實(shí)現(xiàn)。
而在native層中g(shù)etContextObject方法通過ProcessState把 創(chuàng)建的對(duì)象BpBinder轉(zhuǎn)換為Java層的IBinder對(duì)象。

IBinder只是一個(gè)接口類,
在native層,IBinder實(shí)現(xiàn)類為BpBinder(由ProcessState創(chuàng)建)
而在Java層,IBinder實(shí)現(xiàn)類則是BinderProxy。

前面調(diào)用IBinder.transact內(nèi)部調(diào)用流程

Paste_Image.png

發(fā)現(xiàn)繞了個(gè)大圈子,最終處理用戶的Binder請(qǐng)求還是通過IPCThreadState和ProcessState來實(shí)現(xiàn)的。

2.3 ProcessState

關(guān)鍵點(diǎn)在于

  • 保證同一個(gè)進(jìn)程中只有一個(gè)ProcessState實(shí)例存在,而且只有在ProcessState對(duì)象創(chuàng)建時(shí)才打開Binder設(shè)置(open_driver)以及做內(nèi)存映射(mmap)
  • 向上層提供IPC服務(wù)
  • 與IPCThreadState分工合作,各司其職

可以看到ProcessState它是一個(gè)進(jìn)程中的單實(shí)例,而IPCThreadState則是線程中的單實(shí)例
可以說,IPCThreadState負(fù)責(zé)與Binder驅(qū)動(dòng)進(jìn)行具體的命令交互,因而它的transact函數(shù)非常重要,
而ProcessState只是負(fù)責(zé)打開了Binder節(jié)點(diǎn)并做mmap

三.Binder Client客戶端

Binder的最大消費(fèi)者是JAVA層的應(yīng)用程序。一般情況下他們可以在程序代碼的任何位置通過bindService,startActivity以及sendBroadcast等一系列接口方法來實(shí)現(xiàn)與其他進(jìn)程的交互。

有了Binder驅(qū)動(dòng),Service Manager的努力,以及Android系統(tǒng)專門面向應(yīng)用開發(fā)提供的Binder強(qiáng)有力的封裝,才能使應(yīng)用程序之間順利地進(jìn)行無縫通信。

3.1 例子bindService

應(yīng)用程序如何能依托bindService來啟動(dòng)系統(tǒng)中其他進(jìn)程提供的Service呢

  • ①應(yīng)用程序填寫Intent,調(diào)用bindService發(fā)出請(qǐng)求

  • ②收到請(qǐng)求的bindService(此時(shí)還在應(yīng)用程序的運(yùn)行空間中)將與ActivityManagerService(AMS)取得聯(lián)系。根據(jù)前面的分析,為了獲得AMS的Binder句柄,我們還要事先調(diào)用ServiceManager.getService,這里就涉及到進(jìn)程間通信了。在得到AMS的句柄值后,程序才能真正地向它發(fā)起請(qǐng)求。

  • ③AMS基于特定的最優(yōu)匹配策略,從其內(nèi)部存儲(chǔ)的系統(tǒng)所有服務(wù)組件的資料中找到與Intent最匹配的一個(gè),然后向它發(fā)送Service綁定請(qǐng)求。如果目標(biāo)進(jìn)程還不存在的話,AMS還要負(fù)責(zé)把它啟動(dòng)起來。

  • ④被綁定的服務(wù)進(jìn)程需要響應(yīng)綁定,執(zhí)行具體的操作,并在成功完成后通知AMS,然后由AMS再回調(diào)發(fā)起請(qǐng)求的應(yīng)用程序(回調(diào)接口是ServiceConnection)

當(dāng)Activity調(diào)用bindService時(shí),最終調(diào)用到的是ContextImpl.bindService

 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
            UserHandle user) {
        ...
            int res = ActivityManagerNative.getDefault().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
        ...
    }

那么,應(yīng)用程序又是如何找到AMS并與之建立聯(lián)系的呢?
和ServiceManager一樣,AMS也同樣提供了ActivityManagerNative和ActivityManagerProxy代理

//ActivityManagerNative.getDefault()
static public IActivityManager getDefault() {
        return gDefault.get();
    }

這個(gè)gDefault.get又是什么

  private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            ...
            IActivityManager am = asInterface(b);
            ...
            return am;
        }
    };

可以看到這里是通過單例模式來創(chuàng)建一個(gè)IActivityManager對(duì)象。
之后通過ServiceManager.getService("activity")方法取得ActivityManagerService的IBinder對(duì)象。
接著調(diào)用asInterface,通過這個(gè)IBinder創(chuàng)建一個(gè)可用的ActivityManagerProxy

static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ActivityManagerProxy(obj);
    }

此外,ActivityManagerNative的另一個(gè)作用是為ActivityManagerService的實(shí)現(xiàn)提供便利。AMS只需要繼承自ActivityManagerNative就可以將用戶的業(yè)務(wù)請(qǐng)求碼與自己的內(nèi)部實(shí)現(xiàn)函數(shù)連接起來。

Paste_Image.png

四.AIDL——Android接口描述語言

它是用于定義客戶端/服務(wù)端通信接口的一種描述語言。

要知道Server Manager本身也是一個(gè)BinderServer,AMS,WMS也是一個(gè)BinderServer,那么從Service Manager的實(shí)現(xiàn)來看,構(gòu)建一個(gè)Binder Server所需的工作為:

1.啟動(dòng)的時(shí)機(jī)

比如SM是開機(jī)的時(shí)候通過init.rc文件啟動(dòng)的,這就保證了它是系統(tǒng)中第一個(gè)注冊成服務(wù)大管家的Server,而WMS,AMS這些則是在SystemServer中啟動(dòng)的。

2.提供一致的服務(wù)接口

一個(gè)Binder Server應(yīng)該向公眾暴露它所能提供的服務(wù),而且客戶端使用的服務(wù)接口和服務(wù)器實(shí)現(xiàn)的服務(wù)接口必須是完全一致的。

對(duì)于WMS
通過分析aidl文件以及由它轉(zhuǎn)換生成的java接口文件,我們知道一個(gè)AIDL接口包括了IwindowManager,IWindowManager.Stub和IWindowManagerManager.Stub.Proxy三個(gè)重要類。
后兩者分別面向與WMS的服務(wù)端和Binder Client本地代理的實(shí)現(xiàn),且都繼承與IWindowManager,因而就保證了Client和Server是在完全一致的服務(wù)接口上進(jìn)行通訊。

3.與Binder驅(qū)動(dòng)的交互

一個(gè)Binder Server需要與Binder驅(qū)動(dòng)做哪些交互呢?除去一系列必要的初始化以外(open,mmap等),就是要通過不斷地ioctl來循環(huán)讀寫數(shù)據(jù)。

比如SM就是通過binder_loop函數(shù)中的一個(gè)for死循環(huán)來完成這一工作的。
而對(duì)于AMS,PMS這些,他們在SystemServer啟動(dòng)的過程中會(huì)開啟一個(gè)線程來循環(huán)讀取消息,這點(diǎn)和binder_loop的主體框架基本一致。

4.外界如何才能訪問到這個(gè)Server服務(wù)?

  • ①Service在ServiceManager中注冊
    這種方法普遍存在于Android系統(tǒng)服務(wù)中,如ActivityManagerService,WindowManagerService等都在ServiceManager中做了注冊。調(diào)用者只需要通過ServiceManager.getService(name)就可以獲取到Binder Server的本地代理,然后與之通信。

WMS可以通過addService把自己注冊到Service Manager中。因而任何Binder Client都可以通過SM的getService接口獲取到它的一個(gè)引用。稱為實(shí)名的Server。

  • ②通過其他Server作為中介
    也就是Binder Server不需要在ServiceManager中注冊,只需要通過一個(gè)第三方的實(shí)名Server。
    比如WindowSession。
    匿名Server安全性高。

更簡單的來構(gòu)建Binder Server就是使用AIDL.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容