聊聊怎樣學(xué)習(xí)Binder

前言

Binder可以說是整個(gè)Android框架最重要的一個(gè)基礎(chǔ)。如果不能吃透Binder,就談不上對(duì)Android有多么深刻的理解。這個(gè)道理相信大部分Android開發(fā)者都清楚。但是Binder整個(gè)框架又看起來好像深不可測(cè),上至Java,下至kernel。又橫跨眾多進(jìn)程和服務(wù)。想看看源代碼來學(xué)習(xí)??粗粗涂床幻靼琢?,不由得發(fā)出“我是誰?我在哪里?”這樣的疑問。這篇文章就想從作者個(gè)人的經(jīng)驗(yàn)來聊聊如何學(xué)習(xí)Binder。

Binder的學(xué)習(xí)

Binder的學(xué)習(xí)和學(xué)習(xí)其他源代碼一樣并沒有什么特殊的訣竅。那就是硬著頭皮看源碼,看文章。反復(fù)的看,不停的想,至少把妨礙自己理解的堵塞點(diǎn)都搞懂了,整個(gè)鏈路從上到下,從下到上都無障礙的跑通了那才算真正掌握了Binder。當(dāng)然這也是學(xué)習(xí)所有其他框架源碼的要求。關(guān)于學(xué)習(xí)源碼的更多方法,可以參考我的另一篇文章我是如何學(xué)習(xí)Flutter源碼的。

源碼浩如煙海,看著看著就不知道跑哪里去了,沒有關(guān)系。可以參考前人的經(jīng)驗(yàn)來為自己帶路。然而互聯(lián)網(wǎng)上說Binder的文章可謂多如牛毛。如何選擇呢?這里我推薦一位大牛的Binder系列文章。一遍看不懂那就多看幾遍,而且要結(jié)合Android源碼去看。文章里的每一個(gè)代碼片段最好都自己去源碼里對(duì)照著研究。這個(gè)一是為了加深記憶,二是為了促進(jìn)消化。

如果全搞明白了,那么恭喜你,這篇文章剩下的內(nèi)容不用再看了。如果是那種剛看完覺得自己都會(huì)了,過一段時(shí)間別人問你你又說不出來個(gè)子丑寅卯。這種情況是因?yàn)槟阒皇撬烙浻脖秤涀×岁P(guān)于Binder的一些東西而沒有完全理解Binder,有一些關(guān)于Binder通信的關(guān)鍵點(diǎn)沒有搞清楚,知其然而不知其所以然。處于“半懂不懂”的狀態(tài)。如果此時(shí)放棄學(xué)習(xí)Binder那就太可惜了。

寫這篇文章呢,就是希望能用盡量少的搬運(yùn)源碼的方式來捋一下Binder。希望能幫你疏通堵點(diǎn),徹底理解Binder。

正如我在前言里說的,Binder深不可測(cè),上至Java,下至kernel。而程序員似乎會(huì)有個(gè)習(xí)慣,不太喜歡去挖自己領(lǐng)域之外的東西,喜歡Java的就不想去看C/C++的東西,搞APP的,搞Framework的不想去瞅一眼kernel里面長(zhǎng)啥樣?!拔抑灰肋@東西API是啥,會(huì)用就行了。。?!?。某些情況下這可能是沒什么問題的。但不適用于Binder。

要徹底理解Binder,必須要理解Binder驅(qū)動(dòng)(Driver)。

很多對(duì)Binder一知半解的同學(xué)通常都是卡在這個(gè)地方了,不是說硬記住那幾個(gè)“BC_TRANSACTION”,“BR_REPLY”命令,然后交互流程是啥樣就可以了。但如果此時(shí)你把這些東西也忘了,這篇文章也就不用往下看了,這邊建議你回頭把上面說的Binder系列文章擼幾遍再來。

如果你現(xiàn)在處于“半懂不懂”的狀態(tài),那么接著看下面的章節(jié)能不能幫到你來理解Binder。

Binder的理解

本章節(jié)假設(shè)讀者對(duì)Binder架構(gòu)已經(jīng)有了初步的了解。所以并不會(huì)很詳細(xì)的搬源碼。大部分東西都會(huì)直接拿來用。涉及驅(qū)動(dòng)的部分也不會(huì)做詳盡說明,只會(huì)把一些關(guān)鍵點(diǎn)串起來。文中也不會(huì)涉及Java Binder。Java Binder在這里完全是干擾。

概述

這里首先列一下Binder中的一些內(nèi)容:

  • 除了service_manager,其他應(yīng)用級(jí)進(jìn)程/線程,和Binder驅(qū)動(dòng)打交道的都是IPCThreadState。
  • 我們只關(guān)注進(jìn)程間通信的核心流程:transaction。
  • 傳輸發(fā)起者需要告訴Binder三個(gè)東西:handle(你找誰?), cmd(你要干啥?)和data(東西呢?)。
  • 在客戶端handleBpBinder持有。不管什么樣的服務(wù)在Binder眼里就是一串?dāng)?shù)字。記住這一點(diǎn)。
  • 在server端真正的服務(wù)是BBinder來完成的。
error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
                            &reply, tr.flags);
  • 顯然Binder驅(qū)動(dòng)幫我們完成了從本地的一串?dāng)?shù)字handle到遠(yuǎn)端實(shí)體服務(wù)BBinder的轉(zhuǎn)換。

這里就引出了幾個(gè)問題:

  1. 本地handle是如何轉(zhuǎn)換為遠(yuǎn)端BBinder的?
  2. 這個(gè)handle是從哪里來的呢?

第二個(gè)問題可能有些同學(xué)會(huì)說是從service_manager那里查來的,然而并沒有這么簡(jiǎn)單。這個(gè)問題我們稍后再解釋。這里先假設(shè)我們已經(jīng)有了這個(gè)handle來說明第一個(gè)問題。

Binder驅(qū)動(dòng)傳輸流程

這個(gè)流程其實(shí)就只有三步:第一,客戶端把數(shù)據(jù)寫進(jìn)來。第二,把數(shù)據(jù)從客戶端傳到服務(wù)端。第三,通知服務(wù)端把數(shù)據(jù)讀走。

這個(gè)流程我希望大家能緊盯著handle因?yàn)樗俏覀兝斫鈫栴}1的唯一線索。

客戶端把數(shù)據(jù)寫進(jìn)來

在用戶空間,函數(shù)IPCThreadState::writeTransactionData()負(fù)責(zé)寫入驅(qū)動(dòng)。
這個(gè)函數(shù)會(huì)構(gòu)造一個(gè)binder_transaction_data的結(jié)構(gòu)體tr。我們的handle會(huì)被寫進(jìn)去。

status_t IPCThreadState::writeTransactionData(...
    int32_t handle, ...)
{
    binder_transaction_data tr;

   ...
   // handle在這里
    tr.target.handle = handle;
    tr.code = code;
   ...
}

然后就到了驅(qū)動(dòng)那里

static int binder_thread_write(...) {
...
//我們只關(guān)注BC_TRANSACTION
case BC_TRANSACTION:
case BC_REPLY: {
            struct binder_transaction_data tr;
            //tr到了這里
            if (copy_from_user(&tr, ptr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
            //這個(gè)函數(shù)做傳輸工作
            binder_transaction(proc, thread, &tr,
                       cmd == BC_REPLY, 0);
            break;
        }
 ...
}

寫進(jìn)來的可能是各種命令,我們只關(guān)注transaction。

把數(shù)據(jù)從客戶端傳到服務(wù)端

這事是由函數(shù)binder_transaction()來干的。注意看一下那幾個(gè)入?yún)ⅲ?code>proc代表客戶端進(jìn)程。thread代表客戶端線程,tr呢就是最開始的那個(gè),記住里面放著handle

傳輸?shù)那疤崾且业侥繕?biāo)的進(jìn)程\線程。而我們唯一的線索就是handle。怎么通過handle來獲得目標(biāo)進(jìn)程\線程呢?

static void binder_transaction(...) {
...
if (tr->target.handle) {
            //通過handle查找目標(biāo)引用
            target_ref = binder_get_ref(proc, tr->target.handle,
                         true);
            //通過目標(biāo)引用得到目標(biāo)節(jié)點(diǎn)。
            target_node = target_ref->node;
        }
        ...
}
//從node里拿到目標(biāo)進(jìn)程的信息
target_proc = target_node->proc;
...
//然后搞了個(gè)binder_transaction的結(jié)構(gòu)體
t = kzalloc(sizeof(*t), GFP_KERNEL);
...
//設(shè)置目標(biāo)進(jìn)程/線程
t->to_proc = target_proc;
t->to_thread = target_thread;
...
//設(shè)置目標(biāo)節(jié)點(diǎn)
t->buffer->target_node = target_node;
...
//要干的活是傳輸
t->work.type = BINDER_WORK_TRANSACTION;
...
//把傳輸任務(wù)插入到目標(biāo)進(jìn)程/線程的todo隊(duì)列。
binder_proc_transaction(t, target_proc, target_thread);
...

把數(shù)據(jù)從客戶端傳到服務(wù)端階段就完成了。從上述關(guān)鍵代碼我們可以看到,要完成傳輸就需要拿到目標(biāo)進(jìn)程的信息。而我們只有handle。驅(qū)動(dòng)會(huì)先拿這個(gè)handle在自身進(jìn)程信息proc內(nèi)查找節(jié)點(diǎn)的應(yīng)用,然后由這個(gè)引用就能得到節(jié)點(diǎn),得到節(jié)點(diǎn)就能拿到目標(biāo)進(jìn)程的信息target_proc了。那么ref是啥?node又是啥呢?為什么他們能通過handle找的到呢?

node可以說是Binder驅(qū)動(dòng)的核心數(shù)據(jù)結(jié)構(gòu)。node會(huì)以紅黑樹的形式保存在服務(wù)端進(jìn)程結(jié)構(gòu)體內(nèi),客戶端則保存的是它的引用ref。而handle來自ref->desc。

noderef是什么時(shí)候保存到Binder驅(qū)動(dòng)中的呢?這個(gè)我們?cè)诤竺娼鉀Qhandle從哪里來的問題再解釋。

所以在傳輸階段我們看到handle換成了node。在插入到目標(biāo)進(jìn)程todo隊(duì)列的數(shù)據(jù)里只有node而沒有handle。handle已經(jīng)完成了它的使命,接下來讓我們關(guān)注node。

通知服務(wù)端把數(shù)據(jù)讀走

static int binder_thread_read(...) {

...
//有活干了
w = list_first_entry(...);
...
//看看是什么活,我們只關(guān)心transaction
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
            t = container_of(w, struct binder_transaction, work);
        } break;
}
...
if (t->buffer->target_node) {
            tr.target.ptr = target_node->ptr;
            tr.cookie =  target_node->cookie;
            ...
            cmd = BR_TRANSACTION;
        }
        ...
        tr.code = t->code;
}

cmd設(shè)置成了BR_TRANSACTIONbinder_thread_read()會(huì)拼一個(gè)binder_transaction_data。也就是tr出來。它是要被送往用戶空間的,就像之前從用戶空間往binder驅(qū)動(dòng)里寫的也是一個(gè)tr一樣。這里注意驅(qū)動(dòng)把node里的兩個(gè)東西設(shè)置給了tr。ptrcookie。記住它們。

接下來就回到用戶空間了:

status_t IPCThreadState::executeCommand(int32_t cmd) {

//我們只關(guān)心傳輸
  case BR_TRANSACTION:
        {
            if (tr.target.ptr) {
                if (reinterpret_cast<RefBase::weakref_type*>(
                        tr.target.ptr)->attemptIncStrong(this)) {
                    error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
                            &reply, tr.flags);
                    reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
                } 
        }
        break;
}

啊哈,這里我們就能看出來原來cookie的真身就是BBinder。也就是我們服務(wù)的真身了啊。

所以捋一下Binder驅(qū)動(dòng)的傳輸過程就是從客戶端的handle在自身進(jìn)程信息proc里查找ref。再通過ref就能得到node。而node則是存儲(chǔ)在服務(wù)端進(jìn)程信息里的。node就很關(guān)鍵了,從它身上能找到目標(biāo)進(jìn)程的信息和目標(biāo)服務(wù)的真身BBinder實(shí)例。然后回到用戶區(qū)直接調(diào)用就好了。

傳輸過程就介紹完了,接下來我們還有解決另外一個(gè)系列問題,handle從從哪里來?node又是什么時(shí)候給保存到內(nèi)核里去的呢?

對(duì)了,這就涉及到services_manager了。

服務(wù)注冊(cè)及獲取

我們知道services_manager是個(gè)特殊的服務(wù)。有點(diǎn)像個(gè)DNS服務(wù)器,其他什么AMS,PMS等等都需要先到它那里注冊(cè)以后,客戶們才能在services_manager找到他們。既然services_manager這么特殊,那它在驅(qū)動(dòng)中肯定會(huì)有個(gè)獨(dú)一無二的node吧。

所以services_manager啟動(dòng)的時(shí)候會(huì)給Binder驅(qū)動(dòng)發(fā)這么個(gè)命令BINDER_SET_CONTEXT_MGR。
Binder驅(qū)動(dòng)照辦:

static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
    ...
    temp = binder_new_node(proc, 0, 0);
    ...
    context->binder_context_mgr_node = temp;
    binder_put_node(temp);
...
}

函數(shù)binder_new_node()是用來新建node

static struct binder_node *binder_new_node(struct binder_proc *proc,
                       binder_uintptr_t ptr,
                       binder_uintptr_t cookie)

三個(gè)入?yún)ⅲ?code>proc,ptrcookie。回想一下上面介紹傳輸過程的時(shí)候這三個(gè)參數(shù)是不是都用到過?當(dāng)然,對(duì)于services_manager來說只需要進(jìn)程信息,其他兩個(gè)入?yún)⒍际?。這個(gè)特殊的node被設(shè)置給context->binder_context_mgr_node

接下來讓我們看一下服務(wù)注冊(cè)過程:

注冊(cè)服務(wù)

注冊(cè)服務(wù)的要點(diǎn)是盯住服務(wù)實(shí)體Binder是如何轉(zhuǎn)化為node的。

我們都知道service_manager特殊,特殊就特殊在它的handle就是0。所以無論是注冊(cè)服務(wù)還是獲取服務(wù),我們不需要去別處問,自己就可以搞出來個(gè)代表service_managerBpBinder。注冊(cè)的時(shí)候是這樣的

virtual status_t addService(const String16& name, const sp<IBinder>& service,
                                bool allowIsolated, int dumpsysPriority) {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        data.writeInt32(dumpsysPriority);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }

注意這句data.writeStrongBinder(service);也就是說把服務(wù)自身給寫到要傳輸?shù)臄?shù)據(jù)里面去了。
寫成啥樣了呢?

status_t flatten_binder(){
...
flat_binder_object obj;
...
 IBinder *local = binder->localBinder();
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.hdr.type = BINDER_TYPE_HANDLE;
            obj.binder = 0;
            obj.handle = handle;
            obj.cookie = 0;
        } else {
            obj.hdr.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);
        }
...
}

因?yàn)檫@里是注冊(cè)服務(wù),所以給的Binder是實(shí)體Binder,走的是else分支。類型為BINDER_TYPE_BINDER,cookie設(shè)置為service實(shí)體。

注冊(cè)服務(wù)是一次handle為0的Binder傳輸?;叵胛覀冎坝懻摰膫鬏斶^程,handle如果是0的話會(huì)在傳輸過程走不一樣的分支:

static void binder_transaction(...) {
...
if (tr->target.handle) {
            ...
        } else {
            target_node = context->binder_context_mgr_node;
        }
//從node里拿到目標(biāo)進(jìn)程的信息
target_proc = target_node->proc;

...
        switch (hdr->type) {
        case BINDER_TYPE_BINDER:
        case BINDER_TYPE_WEAK_BINDER: {
            struct flat_binder_object *fp;
            fp = to_flat_binder_object(hdr);
            ret = binder_translate_binder(fp, t, thread);
        } break;
        case BINDER_TYPE_HANDLE:
        case BINDER_TYPE_WEAK_HANDLE: {
            struct flat_binder_object *fp;
            fp = to_flat_binder_object(hdr);
            ret = binder_translate_handle(fp, t, thread);
        } break;

...

//把傳輸任務(wù)插入到目標(biāo)進(jìn)程/線程的todo隊(duì)列。
binder_proc_transaction(t, target_proc, target_thread);
...

對(duì)于注冊(cè)服務(wù)的流程來說在傳輸?shù)臅r(shí)候有兩個(gè)地方需要注意,

  1. 由于handle是0,所以直接會(huì)找到binder_context_mgr_node。這個(gè)node也就是我們之前看到的service_manager初始化的時(shí)候創(chuàng)建的,也就是說我們直接就扎到了service_manager。
  2. 還記得我們之前把服務(wù)實(shí)體放在傳輸數(shù)據(jù)里面了嗎?這里驅(qū)動(dòng)會(huì)檢查傳輸?shù)臄?shù)據(jù),如果發(fā)現(xiàn)有BINDER_TYPE_XXX類型的都要做轉(zhuǎn)換,為啥要轉(zhuǎn)呢?因?yàn)槲业膶?shí)體到你那里就得是個(gè)handle啊, 否則都不在同一個(gè)進(jìn)程,不轉(zhuǎn)換的話你拿我的實(shí)體也沒用啊。所以,這里我們的服務(wù)實(shí)體要做一次轉(zhuǎn)換了:
static int binder_translate_binder(struct flat_binder_object *fp,
                   struct binder_transaction *t,
                   struct binder_thread *thread)
{
    struct binder_node *node;
    struct binder_ref *ref;
    struct binder_proc *proc = thread->proc;
    struct binder_proc *target_proc = t->to_proc;

    node = binder_get_node(proc, fp->binder);
    if (!node) {
        node = binder_new_node(proc, fp->binder, fp->cookie);
    }

    ref = binder_get_ref_for_node(target_proc, node, &thread->todo);

    if (fp->hdr.type == BINDER_TYPE_BINDER)
        fp->hdr.type = BINDER_TYPE_HANDLE;
    else
        fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
    fp->binder = 0;
    fp->handle = ref->desc;
    fp->cookie = 0;
...
}

注意看這里的轉(zhuǎn)換,首先是在自己的node樹里面找有沒有實(shí)體服務(wù)Binder對(duì)應(yīng)的node。這里因?yàn)槲覀兪且?cè)服務(wù),所以node不存在,需要新建一個(gè)。你看至此我們的新服務(wù)已經(jīng)有了自己的node了。然后呢會(huì)在目標(biāo)進(jìn)程信息,也就是service_manager中找這個(gè)node的引用,沒有的話會(huì)新建一個(gè)引用的,這個(gè)過程會(huì)產(chǎn)生一個(gè)數(shù)放在ref->desc。對(duì)了,這個(gè)數(shù)就是我們的新服務(wù)在service_manager那邊的handle了。最后就是對(duì)原始數(shù)據(jù)做變身了。類型從BINDER變?yōu)?code>HANDLE。清空bindercookie。給handle字段賦值,我們的實(shí)體Binder就這樣華麗的變成自己的分身了。

千萬不要搞混了,這里做轉(zhuǎn)換的是我們要注冊(cè)的服務(wù),完全是處在事務(wù)數(shù)據(jù)中。而傳輸這些事務(wù)數(shù)據(jù)的是handle為0,對(duì)應(yīng)node是binder_context_mgr_node的事務(wù)。

然后就是service_manager那邊讀取傳輸?shù)臄?shù)據(jù)。拿到那個(gè)轉(zhuǎn)換以后的fp->handle和服務(wù)的名字一起保存起來。

總體而言注冊(cè)服務(wù):

  • 是一次對(duì)service_manager的傳輸,只不過需要在開始的時(shí)候把服務(wù)實(shí)體Binder放在要傳輸?shù)臄?shù)據(jù)里。
  • Binder驅(qū)動(dòng)會(huì)在傳輸過程中做一次從實(shí)體服務(wù)Binder到對(duì)應(yīng)handle的變換。
  • 在這個(gè)變換的過程中會(huì)創(chuàng)建實(shí)體服務(wù)Binder對(duì)應(yīng)的node。

接下來我們看一下獲取服務(wù)的過程。貌似應(yīng)該已經(jīng)一目了然了。客戶端拿著服務(wù)名字去service_manager那里去請(qǐng)求handle。拿到以后就可以實(shí)例化BpBinder去調(diào)用服務(wù)了。

獲取服務(wù)

獲取服務(wù)的過程分兩段,一個(gè)是把服務(wù)名字傳過去,另一個(gè)是把handle傳回來。去程沒啥好說的,和上面的注冊(cè)服務(wù)過程差不多,還少了中間做轉(zhuǎn)換的步驟?;爻叹褪遣灰粯拥牟僮髁?。service_manager把查到的handle通過BC_REPLY給送回來的。別看handle只是個(gè)數(shù),在傳輸?shù)臅r(shí)候必須把它包裝一下,變成這樣的:

    obj->hdr.type = BINDER_TYPE_HANDLE;
    obj->handle = handle;
    obj->cookie = 0;

類型是BINDER_TYPE_HANDLE。

這個(gè)數(shù)據(jù)結(jié)構(gòu)會(huì)寫入Binder驅(qū)動(dòng),命令BC_REPLY會(huì)走到和BC_TRANSACTION一樣的函數(shù),也就是binder_transaction(),但是某些分支有有所不同。

binder_transaction(...){

if (reply) {
in_reply_to = thread->transaction_stack;
target_thread = in_reply_to->from;
target_proc = target_thread->proc;
}
...
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
            struct flat_binder_object *fp;
            fp = to_flat_binder_object(hdr);
            ret = binder_translate_handle(fp, t, thread);
        } break;
        ...
}

可見返回過程不需要再查找node因?yàn)檎{(diào)用端是誰是清楚的。直接把from變成target就行了。

因?yàn)榉祷財(cái)?shù)據(jù)塊里面放著獲取到的服務(wù)handle。同樣的這里會(huì)給它做個(gè)變換。

看一下binder_translate_handle()如何做變換:

static int binder_translate_handle(struct flat_binder_object *fp,
                   struct binder_transaction *t,
                   struct binder_thread *thread)
{
    struct binder_ref *ref;
    struct binder_proc *proc = thread->proc;
    struct binder_proc *target_proc = t->to_proc;

    ref = binder_get_ref(proc, fp->handle,
                 fp->hdr.type == BINDER_TYPE_HANDLE);

    if (ref->node->proc == target_proc) {
        if (fp->hdr.type == BINDER_TYPE_HANDLE)
            fp->hdr.type = BINDER_TYPE_BINDER;
        else
            fp->hdr.type = BINDER_TYPE_WEAK_BINDER;
        fp->binder = ref->node->ptr;
        fp->cookie = ref->node->cookie;
        
    } else {
        struct binder_ref *new_ref;
        new_ref = binder_get_ref_for_node(target_proc, ref->node, NULL);
        fp->binder = 0;
        fp->handle = new_ref->desc;
        fp->cookie = 0;
    }
}

這里的轉(zhuǎn)換有兩個(gè)分支,如果此服務(wù)進(jìn)程和目標(biāo)進(jìn)程相同,則將handle轉(zhuǎn)換成實(shí)體Binder。也就是說你去service_manager那里獲取到了和你同一個(gè)進(jìn)程的服務(wù),自然可以轉(zhuǎn)換為實(shí)體Binder直接拿來用了。

如果不是同一個(gè)進(jìn)程呢?則會(huì)到目標(biāo)進(jìn)程信息那里去獲取一個(gè)新的handle。替換掉原來的handle返回給調(diào)用者。

為什么會(huì)這樣呢?這就需要深入了解noderef的關(guān)系。

  • node實(shí)體只保存在創(chuàng)建者進(jìn)程所對(duì)應(yīng)的proc中。
  • ref是保存在當(dāng)前進(jìn)程中對(duì)外部node實(shí)體的引用,不同的進(jìn)程對(duì)同一個(gè)noderef是不同的。
  • 所以可以認(rèn)為handle是進(jìn)程私有的,不要想當(dāng)然的認(rèn)為從service_manager拿到的handle就是和自己進(jìn)程拿到的一樣。

總結(jié)

Binder雖然結(jié)構(gòu)復(fù)雜,難以全面掌握。但有時(shí)又是Android開發(fā)者在實(shí)際工作中不得不面對(duì)的問題。掌握Binder需要投入大量精力和努力。通過一些文章的指引,是可以在學(xué)習(xí)Binder的路途上減輕一些壓力,避免一些誤區(qū)的。本文就希望能幫助開發(fā)者打通一些在學(xué)習(xí)Binder時(shí)可能會(huì)遇到的阻塞點(diǎn),特別是這些阻塞點(diǎn)基本上會(huì)存在于Binder驅(qū)動(dòng)層。畢竟樓再高,蓋起來還是要先從地基打起,Binder驅(qū)動(dòng)就是Binder這座大廈的基礎(chǔ)。打好這個(gè)基礎(chǔ),往上的就都是包裹和通道了,理解起來也就會(huì)比較輕松。希望能對(duì)大家有所幫助。

?著作權(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),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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