1. 程式人生 > >深入理解Binder通訊原理及面試問題

深入理解Binder通訊原理及面試問題

Binder承擔了絕大部分Android程序通訊的職責,可以看做是Android的血管系統,負責不同服務模組程序間的通訊。在對Binder的理解上,可大可小,日常APP開發並不怎麼涉及Binder通訊知識,最多就是Service及AIDL的使用會涉及部分Binder知識。Binder往小了說可總結成一句話:一種IPC程序間通訊方式,負責程序A的資料,傳送到程序B。往大了說,其實涉及的知識還是很多的,如Binder驅動、Android中對於Binder驅動的擴充套件、Zygote程序孵化中對於Binder通訊的支援、Java層Binder封裝,Native層Binder通訊的封裝等等,Binder訃告機制等等。網上很多分析Binder框架的文章,一般都是從ServiceManager、Binder驅動、addService、getService來分析等來分析,其實這些主要是針對系統提供的服務,跟我們常見的bindService還是有一定區別的,這些文章一般分析的都很詳細,而且篇幅都很大,缺點就是看的太多,整體下來抓不住一些關鍵的點,看完了,當時能理解個大概,過段時間,基本忘乾淨了。本篇文章主要簡述一些Binder難以理解的點,但不會太細的跟蹤分析,只拋磚,自己去發掘玉:

  • Binder的定向制導,如何找到目標Binder,喚起程序或者執行緒
  • Binder中的紅黑樹,為什麼會有兩棵binder_ref紅黑樹
  • Binder一次拷貝原理(直接拷貝到目標執行緒的核心空間,核心空間與使用者空間對應)
  • Binder傳輸資料的大小限制(核心4M 上層限制1m-8k),傳輸Bitmap過大,就會崩潰的原因,Activity之間傳輸BitMap
  • 系統服務與bindService等啟動的服務的區別
  • Binder執行緒、Binder主執行緒、Client請求執行緒的概念與區別
  • Client是同步而Server是非同步到底說的什麼
  • Android APP程序天生支援Binder通訊的原理是什麼
  • Android APP有多少Binder執行緒,是固定的麼
  • Binder執行緒的睡眠與喚醒(請求執行緒睡在哪個等待佇列上,喚醒目標端哪個佇列上的執行緒)
  • Binder協議中BC與BR的區別
  • Binder在傳輸資料的時候是如何層層封裝的–不同層次使用的資料結構(命令的封裝
  • Binder驅動傳遞資料的釋放(釋放時機)
  • 一個簡單的Binder通訊C/S模型
  • ServiceManager addService的限制(並非服務都能使用ServiceManager的addService)
  • bindService啟動Service與Binder服務實體的流程
  • Java層Binder實體與與BinderProxy是如何例項化及使用的,與Native層的關係是怎樣的
  • Parcel readStrongBinder與writeStrongBinder的原理

Binder如何精確制導,找到目標Binder實體,並喚醒程序或者執行緒

Binder實體服務其實有兩種,一是通過addService註冊到ServiceManager中的服務,比如ActvityManagerService、PackageManagerService、PowerManagerService等,一般都是系統服務;還有一種是通過bindServcice拉起的一些服務,一般是開發者自己實現的服務。這裡先看通過addService新增的被ServiceManager所管理的服務。有很多分析ServiceManager的文章,本文不分析ServiceManager,只是簡單提一下,ServiceManager是比較特殊的服務,所有應用都能直接使用,因為ServiceManager對於Client端來說Handle控制代碼是固定的,都是0,所以ServiceManager服務並不需要查詢,可以直接使用。

理解Binder定向制導的關鍵是理解Binder的四棵紅黑樹,先看一下binder_proc結構體,在它內部有四棵紅黑樹,threads,nodes,refs_by_desc,refs_by_node,nodes就是Binder實體在核心中對應的資料結構,binder_node裡記錄程序相關的binder_proc,還有Binder實體自身的地址等資訊,nodes紅黑樹位於binder_proc,可以知道Binder實體其實是程序內可見,而不是執行緒內。

struct binder_proc {
    struct hlist_node proc_node;
    struct rb_root threads;
    struct rb_root nodes;
    struct rb_root refs_by_desc;
    struct rb_root refs_by_node;
    。。。
    struct list_head todo;
    wait_queue_head_t wait;
    。。。
};

現在假設存在一堆Client與Service,Client如何才能訪問Service呢?首先Service會通過addService將binder實體註冊到ServiceManager中去,Client如果想要使用Servcie,就需要通過getService向ServiceManager請求該服務。在Service通過addService向ServiceManager註冊的時候,ServiceManager會將服務相關的資訊儲存到自己程序的Service列表中去,同時在ServiceManager程序的binder_ref紅黑樹中為Service新增binder_ref節點,這樣ServiceManager就能獲取Service的Binder實體資訊。而當Client通過getService向ServiceManager請求該Service服務的時候,ServiceManager會在註冊的Service列表中查詢該服務,如果找到就將該服務返回給Client,在這個過程中,ServiceManager會在Client程序的binder_ref紅黑樹中新增binder_ref節點,可見本程序中的binder_ref紅黑樹節點都不是本程序自己建立的,要麼是Service程序將binder_ref插入到ServiceManager中去,要麼是ServiceManager程序將binder_ref插入到Client中去。之後,Client就能通過Handle控制代碼獲取binder_ref,進而訪問Service服務。

binder_ref新增邏輯

getService之後,便可以獲取binder_ref引用,進而獲取到binder_proc與binder_node資訊,之後Client便可有目的的將binder_transaction事務插入到binder_proc的待處理列表,並且,如果程序正在睡眠,就喚起程序,其實這裡到底是喚起程序還是執行緒也有講究,對於Client向Service傳送請求的狀況,一般都是喚醒binder_proc上睡眠的執行緒:

struct binder_ref {
    int debug_id;
    struct rb_node rb_node_desc;
    struct rb_node rb_node_node;
    struct hlist_node node_entry;
    struct binder_proc *proc;
    struct binder_node *node;
    uint32_t desc;
    int strong;
    int weak;
    struct binder_ref_death *death;
};

binder_proc為何會有兩棵binder_ref紅黑樹

binder_proc中存在兩棵binder_ref紅黑樹,其實兩棵紅黑樹中的節點是複用的,只是查詢方式不同,一個通過handle控制代碼,一個通過node節點查詢。個人理解:refs_by_node紅黑樹主要是為了
binder驅動往使用者空間寫資料所使用的,而refs_by_desc是使用者空間向Binder驅動寫資料使用的,只是方向問題。比如在服務addService的時候,binder驅動會在在ServiceManager程序的binder_proc中查詢binder_ref結構體,如果沒有就會新建binder_ref結構體,再比如在Client端getService的時候,binder驅動會在Client程序中通過 binder_get_ref_for_node為Client建立binder_ref結構體,並分配控制代碼,同時插入到refs_by_desc紅黑樹中,可見refs_by_node紅黑樹,主要是給binder驅動往使用者空間寫資料使用的。相對的refs_by_desc主要是為了使用者空間往binder驅動寫資料使用的,當用戶空間已經獲得Binder驅動為其建立的binder_ref引用控制代碼後,就可以通過binder_get_ref從refs_by_desc找到響應binder_ref,進而找到目標binder_node。可見有兩棵紅黑樹主要是區分使用物件及資料流動方向,看下面的程式碼就能理解:

// 根據32位的uint32_t desc來查詢,可以看到,binder_get_ref不會新建binder_ref節點
static struct binder_ref *binder_get_ref(struct binder_proc *proc,
                     uint32_t desc)
{
    struct rb_node *n = proc->refs_by_desc.rb_node;
    struct binder_ref *ref;
    while (n) {
        ref = rb_entry(n, struct binder_ref, rb_node_desc);
        if (desc < ref->desc)
            n = n->rb_left;
        else if (desc > ref->desc)
            n = n->rb_right;
        else
            return ref;
    }
    return NULL;
}

可以看到binder_get_ref並具備binder_ref的建立功能,相對應的看一下binder_get_ref_for_node,binder_get_ref_for_node紅黑樹主要通過binder_node進行查詢,如果找不到,就新建binder_ref,同時插入到兩棵紅黑樹中去

static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
                          struct binder_node *node)
{
    struct rb_node *n;
    struct rb_node **p = &proc->refs_by_node.rb_node;
    struct rb_node *parent = NULL;
    struct binder_ref *ref, *new_ref;
    while (*p) {
        parent = *p;
        ref = rb_entry(parent, struct binder_ref, rb_node_node);
        if (node < ref->node)
            p = &(*p)->rb_left;
        else if (node > ref->node)
            p = &(*p)->rb_right;
        else
            return ref;
    }

    // binder_ref 可以在兩棵樹裡面,但是,兩棵樹的查詢方式不同,並且通過desc查詢,不具備新建功能
    new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
    if (new_ref == NULL)
        return NULL;
    binder_stats_created(BINDER_STAT_REF);
    new_ref->debug_id = ++binder_last_id;
    new_ref->proc = proc;
    new_ref->node = node;
    rb_link_node(&new_ref->rb_node_node, parent, p);
    // 插入到proc->refs_by_node紅黑樹中去
    rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
    // 是不是ServiceManager的
    new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1;
    // 分配Handle控制代碼,為了插入到refs_by_desc
    for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
        ref = rb_entry(n, struct binder_ref, rb_node_desc);
        if (ref->desc > new_ref->desc)
            break;
        new_ref->desc = ref->desc + 1;
    }
    // 找到目標位置
    p = &proc->refs_by_desc.rb_node;
    while (*p) {
        parent = *p;
        ref = rb_entry(parent, struct binder_ref, rb_node_desc);
        if (new_ref->desc < ref->desc)
            p = &(*p)->rb_left;
        else if (new_ref->desc > ref->desc)
            p = &(*p)->rb_right;
        else
            BUG();
    }
    rb_link_node(&new_ref->rb_node_desc, parent, p);
    // 插入到refs_by_desc紅黑樹中區
    rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc);

    if (node) {
        hlist_add_head(&new_ref->node_entry, &node->refs);
        binder_debug(BINDER_DEBUG_INTERNAL_REFS,
                 "binder: %d new ref %d desc %d for "
                 "node %d\n", proc->pid, new_ref->debug_id,
                 new_ref->desc, node->debug_id);
    } else {
        binder_debug(BINDER_DEBUG_INTERNAL_REFS,
                 "binder: %d new ref %d desc %d for "
                 "dead node\n", proc->pid, new_ref->debug_id,
                  new_ref->desc);
    }
    return new_ref;
}

該函式呼叫在binder_transaction函式中,其實就是在binder驅動訪問target_proc的時候,這也也很容易理解,Handle控制代碼對於跨程序沒有任何意義,程序A中的Handle,放到程序B中是無效的。

兩棵binder_ref紅黑樹

Binder一次拷貝原理

Android選擇Binder作為主要程序通訊的方式同其效能高也有關係,Binder只需要一次拷貝就能將A程序使用者空間的資料為B程序所用。這裡主要涉及兩個點:

  • Binder的map函式,會將核心空間直接與使用者空間對應,使用者空間可以直接訪問核心空間的資料
  • A程序的資料會被直接拷貝到B程序的核心空間(一次拷貝)

    #define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
    
    ProcessState::ProcessState()
        : mDriverFD(open_driver())
        , mVMStart(MAP_FAILED)
        , mManagesContexts(false)
        , mBinderContextCheckFunc(NULL)
        , mBinderContextUserData(NULL)
        , mThreadPoolStarted(false)
        , mThreadPoolSeq(1){
       if (mDriverFD >= 0) {
        ....
            // mmap the binder, providing a chunk of virtual address space to receive transactions.
            mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
       ...
    
        }
    }
    

    mmap函式屬於系統呼叫,mmap會從當前程序中獲取使用者態可用的虛擬地址空間(vm_area_struct *vma),並在mmap_region中真正獲取vma,然後呼叫file->f_op->mmap(file, vma),進入驅動處理,之後就會在記憶體中分配一塊連續的虛擬地址空間,並預先分配好頁表、已使用的與未使用的標識、初始地址、與使用者空間的偏移等等,通過這一步之後,就能把Binder在核心空間的資料直接通過指標地址對映到使用者空間,供程序在使用者空間使用,這是一次拷貝的基礎,一次拷貝在核心中的標識如下:

    struct binder_proc {
    struct hlist_node proc_node;
    // 四棵比較重要的樹 
    struct rb_root threads;
    struct rb_root nodes;
    struct rb_root refs_by_desc;
    struct rb_root refs_by_node;
    int pid;
    struct vm_area_struct *vma; //虛擬地址空間,使用者控制元件傳過來
    struct mm_struct *vma_vm_mm;
    struct task_struct *tsk;
    struct files_struct *files;
    struct hlist_node deferred_work_node;
    int deferred_work;
    void *buffer; //初始地址
    ptrdiff_t user_buffer_offset; //這裡是偏移
    
    struct list_head buffers;//這個列表連線所有的記憶體塊,以地址的大小為順序,各記憶體塊首尾相連
    struct rb_root free_buffers;//連線所有的已建立對映的虛擬記憶體塊,以記憶體的大小為index組織在以該節點為根的紅黑樹下
    struct rb_root allocated_buffers;//連線所有已經分配的虛擬記憶體塊,以記憶體塊的開始地址為index組織在以該節點為根的紅黑樹下
    
    }
    

上面只是在APP啟動的時候開啟的地址對映,但並未涉及到資料的拷貝,下面看資料的拷貝操作。當資料從使用者空間拷貝到核心空間的時候,是直從當前程序的使用者空間接拷貝到目標程序的核心空間,這個過程是在請求端執行緒中處理的,操作物件是目標程序的核心空間。看如下程式碼:

static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply){
                   ...
        在通過進行binder事物的傳遞時,如果一個binder事物(用struct binder_transaction結構體表示)需要使用到記憶體,
        就會呼叫binder_alloc_buf函式分配此次binder事物需要的記憶體空間。
        需要注意的是:這裡是從目標程序的binder記憶體空間分配所需的記憶體
        //從target程序的binder記憶體空間分配所需的記憶體大小,這也是一次拷貝,完成通訊的關鍵,直接拷貝到目標程序的核心空間
        //由於使用者空間跟核心空間僅僅存在一個偏移地址,所以也算拷貝到使用者空間
        t->buffer = binder_alloc_buf(target_proc, tr->data_size,
            tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
        t->buffer->allow_user_free = 0;
        t->buffer->debug_id = t->debug_id;
        //該binder_buffer對應的事務    
        t->buffer->transaction = t;
        //該事物對應的目標binder實體 ,因為目標程序中可能不僅僅有一個Binder實體
        t->buffer->target_node = target_node;
        trace_binder_transaction_alloc_buf(t->buffer);
        if (target_node)
            binder_inc_node(target_node, 1, 0, NULL);
        // 計算出存放flat_binder_object結構體偏移陣列的起始地址,4位元組對齊。
        offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
           // struct flat_binder_object是binder在程序之間傳輸的表示方式 //
           // 這裡就是完成binder通訊單邊時候在使用者程序同核心buffer之間的一次拷貝動作 //
          // 這裡的資料拷貝,其實是拷貝到目標程序中去,因為t本身就是在目標程序的核心空間中分配的,
        if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {
            binder_user_error("binder: %d:%d got transaction with invalid "
                "data ptr\n", proc->pid, thread->pid);
            return_error = BR_FAILED_REPLY;
            goto err_copy_data_failed;
        }

可以看到binder_alloc_buf(target_proc, tr->data_size,tr->offsets_size, !reply && (t->flags & TF_ONE_WAY))函式在申請記憶體的時候,是從target_proc程序空間中去申請的,這樣在做資料拷貝的時候copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)),就會直接拷貝target_proc的核心空間,而由於Binder核心空間的資料能直接對映到使用者空間,這裡就不在需要拷貝到使用者空間。這就是一次拷貝的原理。核心空間的資料對映到使用者空間其實就是新增一個偏移地址,並且將資料的首地址、資料的大小都複製到一個使用者空間的Parcel結構體,具體可以參考Parcel.cpp的Parcel::ipcSetDataReference函式。

Binder一次拷貝原理.jpg

Binder傳輸資料的大小限制

雖然APP開發時候,Binder對程式設計師幾乎不可見,但是作為Android的資料運輸系統,Binder的影響是全面性的,所以有時候如果不瞭解Binder的一些限制,在出現問題的時候往往是沒有任何頭緒,比如在Activity之間傳輸BitMap的時候,如果Bitmap過大,就會引起問題,比如崩潰等,這其實就跟Binder傳輸資料大小的限制有關係,在上面的一次拷貝中分析過,mmap函式會為Binder資料傳遞對映一塊連續的虛擬地址,這塊虛擬記憶體空間其實是有大小限制的,不同的程序可能還不一樣。

普通的由Zygote孵化而來的使用者程序,所對映的Binder記憶體大小是不到1M的,準確說是 1*1024*1024) - (4096 *2) :這個限制定義在ProcessState類中,如果傳輸說句超過這個大小,系統就會報錯,因為Binder本身就是為了程序間頻繁而靈活的通訊所設計的,並不是為了拷貝大資料而使用的:

#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))

而在核心中,其實也有個限制,是4M,不過由於APP中已經限制了不到1M,這裡的限制似乎也沒多大用途:

static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
    int ret;
    struct vm_struct *area;
    struct binder_proc *proc = filp->private_data;
    const char *failure_string;
    struct binder_buffer *buffer;
    //限制不能超過4M
    if ((vma->vm_end - vma->vm_start) > SZ_4M)
        vma->vm_end = vma->vm_start + SZ_4M;
    。。。
    }

有個特殊的程序ServiceManager程序,它為自己申請的Binder核心空間是128K,這個同ServiceManager的用途是分不開的,ServcieManager主要面向系統Service,只是簡單的提供一些addServcie,getService的功能,不涉及多大的資料傳輸,因此不需要申請多大的記憶體:

int main(int argc, char **argv)
{
    struct binder_state *bs;
    void *svcmgr = BINDER_SERVICE_MANAGER;

        // 僅僅申請了128k
    bs = binder_open(128*1024);
 if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    svcmgr_handle = svcmgr;
    binder_loop(bs, svcmgr_handler);
    return 0;
}   

系統服務與bindService等啟動的服務的區別

服務可分為系統服務與普通服務,系統服務一般是在系統啟動的時候,由SystemServer程序建立並註冊到ServiceManager中的。而普通服務一般是通過ActivityManagerService啟動的服務,或者說通過四大元件中的Service元件啟動的服務。這兩種服務在實現跟使用上是有不同的,主要從以下幾個方面:

  • 服務的啟動方式
  • 服務的註冊與管理
  • 服務的請求使用方式

首先看一下服務的啟動上,系統服務一般都是SystemServer程序負責啟動,比如AMS,WMS,PKMS,電源管理等,這些服務本身其實實現了Binder介面,作為Binder實體註冊到ServiceManager中,被ServiceManager管理,而SystemServer程序裡面會啟動一些Binder執行緒,主要用於監聽Client的請求,並分發給響應的服務實體類,可以看出,這些系統服務是位於SystemServer程序中(有例外,比如Media服務)。在來看一下bindService型別的服務,這類服務一般是通過Activity的startService或者其他context的startService啟動的,這裡的Service元件只是個封裝,主要的是裡面Binder服務實體類,這個啟動過程不是ServcieManager管理的,而是通過ActivityManagerService進行管理的,同Activity管理類似。

再來看一下服務的註冊與管理:系統服務一般都是通過ServiceManager的addService進行註冊的,這些服務一般都是需要擁有特定的許可權才能註冊到ServiceManager,而bindService啟動的服務可以算是註冊到ActivityManagerService,只不過ActivityManagerService管理服務的方式同ServiceManager不一樣,而是採用了Activity的管理模型,詳細的可以自行分析

最後看一下使用方式,使用系統服務一般都是通過ServiceManager的getService得到服務的控制代碼,這個過程其實就是去ServiceManager中查詢註冊系統服務。而bindService啟動的服務,主要是去ActivityManagerService中去查詢相應的Service元件,最終會將Service內部Binder的控制代碼傳給Client。

系統服務與bindService啟動服務的區別.jpg

Binder執行緒、Binder主執行緒、Client請求執行緒的概念與區別

Binder執行緒是執行Binder服務的載體,只對於服務端才有意義,對請求端來說,是不需要考慮Binder執行緒的,但Android系統的處理機制其實大部分是互為C/S的。比如APP與AMS進行互動的時候,都互為對方的C與S,這裡先不討論這個問題,先看Binder執行緒的概念。

Binder執行緒就是執行Binder實體業務的執行緒,一個普通執行緒如何才能成為Binder執行緒呢?很簡單,只要開啟一個監聽Binder字元裝置的Loop執行緒即可,在Android中有很多種方法,不過歸根到底都是監聽Binder,換成程式碼就是通過ioctl來進行監聽。

拿ServerManager程序來說,其主線就是Binder執行緒,其做法是通過binder_loop實現不死執行緒:

void binder_loop(struct binder_state *bs, binder_handler func)
{
   ...
    for (;;) {
    <!--關鍵點1-->
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
     <!--關鍵點2-->
        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
        。。
    }
}

上面的關鍵程式碼1就是阻塞監聽客戶端請求,2 就是處理請求,並且這是一個死迴圈,不退出。再來看SystemServer程序中的執行緒,在Android4.3(6.0以後打程式碼就不一樣了)中SystemSever主執行緒便是Binder執行緒,同時一個Binder主執行緒,Binder執行緒與Binder主執行緒的區別是:執行緒是否可以終止Loop,不過目前啟動的Binder執行緒都是無法退出的,其實可以全部看做是Binder主執行緒,其實現原理是,在SystemServer主執行緒執行到最後的時候,Loop監聽Binder裝置,變身死迴圈執行緒,關鍵程式碼如下:

extern "C" status_t system_init()
{
    ...
    ALOGI("System server: entering thread pool.\n");
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    ALOGI("System server: exiting thread pool.\n");
    return NO_ERROR;
}

ProcessState::self()->startThreadPool()是新建一個Binder主執行緒,而PCThreadState::self()->joinThreadPool()是將當前執行緒變成Binder主執行緒。其實startThreadPool最終也會呼叫joinThreadPool,看下其關鍵函式:

void IPCThreadState::joinThreadPool(bool isMain)
{
    ...
    status_t result;
    do {
        int32_t cmd;
        ...關鍵點1 
        result = talkWithDriver();
        if (result >= NO_ERROR) {
           ...關鍵點2 
            result = executeCommand(cmd);
        }
        // 非主執行緒的可以退出
        if(result == TIMED_OUT && !isMain) {
            break;
        }
        // 死迴圈,不完結,呼叫了這個,就好比是開啟了Binder監聽迴圈,
    } while (result != -ECONNREFUSED && result != -EBADF);
 }

status_t IPCThreadState::talkWithDriver(bool doReceive)
{  
    do {
        ...關鍵點3 
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
   }   

先看關鍵點1 talkWithDriver,其實質還是去掉用ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)去不斷的監聽Binder字元裝置,獲取到Client傳輸的資料後,再通過executeCommand去執行相應的請求,joinThreadPool是普通執行緒化身Binder執行緒最常見的方式。不信,就再看一個MediaService,看一下main_mediaserver的main函式:

int main(int argc, char** argv)
{
   。。。
        sp<ProcessState> proc(ProcessState::self());
        sp<IServiceManager> sm = defaultServiceManager();
        ALOGI("ServiceManager: %p", sm.get());
        AudioFlinger::instantiate();
        MediaPlayerService::instantiate();
        CameraService::instantiate();
        AudioPolicyService::instantiate();
        registerExtensions();
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
    }

其實還是通過joinThreadPool變身Binder執行緒,至於是不是主執行緒,看一下下面的函式:

void IPCThreadState::joinThreadPool(bool isMain)

void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        String8 name = makeBinderThreadName();
        ALOGV("Spawning new pooled thread, name=%s\n", name.string());
        sp<Thread> t = new PoolThread(isMain);
        t->run(name.string());
    }
}

其實關鍵就是就是傳遞給joinThreadPool函式的isMain是否是true,不過是否是Binder主執行緒並沒有什麼用,因為原始碼中並沒有為這兩者的不同處理留入口,感興趣可以去檢視一下binder中的TIMED_OUT。

最後來看一下普通Client的binder請求執行緒,比如我們APP的主執行緒,在startActivity請求AMS的時候,APP的主執行緒成其實就是Binder請求執行緒,在進行Binder通訊的過程中,Client的Binder請求執行緒會一直阻塞,知道Service處理完畢返回處理結果。

Binder請求的同步與非同步

很多人都會說,Binder是對Client端同步,而對Service端非同步,其實並不完全正確,在單次Binder資料傳遞的過程中,其實都是同步的。只不過,Client在請求Server端服務的過程中,是需要返回結果的,即使是你看不到返回資料,其實還是會有個成功與失敗的處理結果返回給Client,這就是所說的Client端是同步的。至於說服務端是非同步的,可以這麼理解:在服務端在被喚醒後,就去處理請求,處理結束後,服務端就將結果返回給正在等待的Client執行緒,將結果寫入到Client的核心空間後,服務端就會直接返回了,不會再等待Client端的確認,這就是所說的服務端是非同步的,可以從原始碼來看一下:

  • Client端同步阻塞請求

    status_t IPCThreadState::transact(int32_t handle,
    uint32_t code, const Parcel& data,
    Parcel* reply, uint32_t flags)
    {

        if (reply) {
            err = waitForResponse(reply);
        } ...
    

Client在請求服務的時候 Parcel* reply基本都是非空的(還沒見過空用在什麼位置),非空就會執行waitForResponse(reply),如果看過幾篇Binder分析文章的人應該都會知道,在A端向B寫完資料之後,A會返回給自己一個BR_TRANSACTION_COMPLETE命令,告知自己資料已經成功寫入到B的Binder核心空間中去了,如果是需要回復,在處理完BR_TRANSACTION_COMPLETE命令後會繼續阻塞等待結果的返回:

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult){
    ...
    while (1) {
    if ((err=talkWithDriver()) < NO_ERROR) break;
     cmd = mIn.readInt32();
    switch (cmd) {
       <!--關鍵點1 -->
      case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
     <!--關鍵點2 -->
        case BR_REPLY:
            {
                binder_transaction_data tr;
                  // free buffer,先設定資料,直接
                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        // 牽扯到資料利用,與記憶體釋放
                        reply->ipcSetDataReference(...)
            }
            goto finish;
    }
 finish:
 ...
return err;
}

關鍵點1就是處理BR_TRANSACTION_COMPLETE,如果需要等待reply,還要通過talkWithDriver等待結果返回,最後執行關鍵點2,處理返回資料。對於服務端來說,區別就在於關鍵點1 ,來看一下服務端Binder執行緒的程式碼,拿常用的joinThreadPool來看,在talkWithDriver後,會執行executeCommand函式,

void IPCThreadState::joinThreadPool(bool isMain)
{
    ...
    status_t result;
    do {
        int32_t cmd;
        ...關鍵點1 
        result = talkWithDriver();
        if (result >= NO_ERROR) {
           ...關鍵點2 
            result = executeCommand(cmd);
        }
        // 非主執行緒的可以退出
        if(result == TIMED_OUT && !isMain) {
            break;
        }
        // 死迴圈,不完結,呼叫了這個,就好比是開啟了Binder監聽迴圈,
    } while (result != -ECONNREFUSED && result != -EBADF);
 }

executeCommand會進一步呼叫sendReply函式,看一下這裡的特點waitForResponse(NULL, NULL),這裡傳遞的都是null,在上面的關鍵點1的地方我們知道,這裡不需要等待Client返回,因此會直接 goto finish,這就是所說的Client同步,而服務端非同步的邏輯。

// BC_REPLY
status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)
{
    // flag 0
    status_t err;
    status_t statusBuffer;
    err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
    if (err < NO_ERROR) return err;
    return waitForResponse(NULL, NULL);
}


case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;

請求同步最好的例子就是在Android6.0之前,國產ROM許可權的申請都是同步的,在申請許可權的時候,APP申請許可權的執行緒會阻塞,就算是UI執行緒也會阻塞,ROM為了防止ANR,都會為許可權申請設定一個倒計時,不操作,就給個預設操作,有興趣可以自己分析。

Android APP程序天生支援Binder通訊的原理是什麼

Android APP程序都是由Zygote程序孵化出來的。常見場景:點選桌面icon啟動APP,或者startActivity啟動一個新程序裡面的Activity,最終都會由AMS去呼叫Process.start()方法去向Zygote程序傳送請求,讓Zygote去fork一個新程序,Zygote收到請求後會呼叫Zygote.forkAndSpecialize()來fork出新程序,之後會通過RuntimeInit.nativeZygoteInit來初始化Andriod APP執行需要的一些環境,而binder執行緒就是在這個時候新建啟動的,看下面的原始碼(Android 4.3):

這裡不分析Zygote,只是給出其大概執行機制,Zygote在啟動後,就會通過runSelectLoop不斷的監聽socket,等待請求來fork程序,如下:

private static void runSelectLoop() throws MethodAndArgsCaller {
    ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
    ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
    FileDescriptor[] fdArray = new FileDescriptor[4];
    ...     
   int loopCount = GC_LOOP_COUNT;
    while (true) {
        int index;
         ...
            boolean done;
            done = peers.get(index).runOnce();
            ...
        }}}

每次fork請求到來都會呼叫ZygoteConnection的runOnce()來處理請求,

boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {

    String args[];
    Arguments parsedArgs = null;
    FileDescriptor[] descriptors;   
        。。。

    try {
       ...關鍵點1 
        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                parsedArgs.niceName);
    } 
    try {
        if (pid == 0) {
            // in child
         ...關鍵點2
            handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
        。。。
    }

runOnce()有兩個關鍵點,關鍵點1 Zygote.forkAndSpecialize就是通過fork系統呼叫來新建程序,**關鍵點2 **handleChildProc就是對新建的APP程序進行一些初始化工作,為Android Java程序建立一些必須的場景。Zygote.forkAndSpecialize沒什麼可看的,就是Linux中的fork程序,這裡主要看一下handleChildProc

private void handleChildProc(Arguments parsedArgs,
        FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
        throws ZygoteInit.MethodAndArgsCaller {

    //從Process.start啟動的parsedArgs.runtimeInit一般都是true            if (parsedArgs.runtimeInit) {
        if (parsedArgs.invokeWith != null) {
            WrapperInit.execApplication(parsedArgs.invokeWith,
                    parsedArgs.niceName, parsedArgs.targetSdkVersion,
                    pipeFd, parsedArgs.remainingArgs);  
        } else {
         // Android應用啟動都走該分支
       RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
                    parsedArgs.remainingArgs); 
       }  
}

接著看 RuntimeInit.zygoteInit函式

public static final void zygoteInit(int targetSdkVersion, String[] argv)
        throws ZygoteInit.MethodAndArgsCaller {

    redirectLogStreams();
    commonInit();
    <!--關鍵點1-->
    nativeZygoteInit();
     <!--關鍵點2-->
    applicationInit(targetSdkVersion, argv);
}

先看關鍵點1,nativeZygoteInit屬於Native方法,該方法位於AndroidRuntime.cpp中,其實就是呼叫呼叫到app_main.cpp中的onZygoteInit

static void com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
    gCurRuntime->onZygoteInit();
}

關鍵就是onZygoteInit

    virtual void onZygoteInit()
    {
        sp proc = ProcessState::self();
        //啟動新binder執行緒loop
        proc->startThreadPool();
    }

首先,ProcessState::self()函式會呼叫open()開啟/dev/binder裝置,這個時候Client就能通過Binder進行遠端通訊;其次,proc->startThreadPool()負責新建一個binder執行緒,監聽Binder裝置,這樣程序就具備了作為Binder服務端的資格。每個APP的程序都會通過onZygoteInit開啟Binder,既能作為Client,也能作為Server,這就是Android程序天然支援Binder通訊的原因。

Android APP程序天然支援Binder通訊.png

Android APP有多少Binder執行緒,是固定的麼?

通過上一個問題我們知道了Android APP執行緒為什麼天然支援Binder通訊,並且可以作為Binder的Service端,同時也對Binder執行緒有了一個瞭解,那麼在一個Android APP的程序裡面究竟有多少個Binder執行緒呢?是固定的嗎。在分析上一個問題的時候,我們知道Android APP程序在Zygote fork之初就為它新建了一個Binder主執行緒,使得APP端也可以作為Binder的服務端,這個時候Binder執行緒的數量就只有一個,假設我們的APP自身實現了很多的Binder服務,一個執行緒夠用的嗎?這裡不妨想想一下SystemServer程序,SystemServer擁有很多系統服務,一個執行緒應該是不夠用的,如果看過SystemServer程式碼可能會發現,對於Android4.3的原始碼,其實一開始為該服務開啟了兩個Binder執行緒。還有個分析Binder常用的服務,media服務,也是在一開始的時候開啟了兩個執行緒。

先看下SystemServer的開始載入的執行緒:通過 ProcessState::self()->startThreadPool()新加了一個Binder執行緒,然後通過IPCThreadState::self()->joinThreadPool();將當前執行緒變成Binder執行緒,注意這裡是針對Android4.3的原始碼,android6.0的這裡略有不同。

extern "C" status_t system_init()
{
    ...
    ALOGI("System server: entering thread pool.\n");
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
    ALOGI("System server: exiting thread pool.\n");
    return NO_ERROR;
}

再看下Media服務,同SystemServer類似,也是開啟了兩個Binder執行緒:

int main(int argc, char** argv)
{      ...
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
 }  

可以看出Android APP上層應用的程序一般是開啟一個Binder執行緒,而對於SystemServer或者media服務等使用頻率高,服務複雜的程序,一般都是開啟兩個或者更多。來看第二個問題,Binder執行緒的數目是固定的嗎?答案是否定的,驅動會根據目標程序中是否存在足夠多的Binder執行緒來告訴程序是不是要新建Binder執行緒,詳細邏輯,首先看一下新建Binder執行緒的入口:

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;
    RefBase::weakref_type* refs;
    status_t result = NO_ERROR;
    switch (cmd) {
    ...
    // 可以根據核心返回資料建立新的binder執行緒
    case BR_SPAWN_LOOPER:
        mProcess->spawnPooledThread(false);
        break;
}

executeCommand一定是從Bindr驅動返回的BR命令,這裡是BR_SPAWN_LOOPER,什麼時候,Binder驅動會向程序傳送BR_SPAWN_LOOPER呢?全域性搜尋之後,發現只有一個地方binder_thread_read,如果直觀的想一下,什麼時候需要新建Binder執行緒呢?很簡單,不夠用的時候,注意上面使用的是spawnPooledThread(false),也就是說這裡啟動的都是普通Binder執行緒。為了瞭解啟動時機,先看一些binder_proc內部判定引數的意義:

struct binder_proc {
    ...
    int max_threads;                // 程序所能啟動的最大非主Binder執行緒數目
    int requested_threads;          // 請求啟動的非主執行緒數
    int requested_threads_started;//已經啟動的非主執行緒數
    int ready_threads;              // 當前可用的Binder執行緒數
    ...
};

再來看binder_thread_read函式中是麼時候會去請求新建Binder執行緒,以Android APP程序為例子,通過前面的分析知道APP程序天然支援Binder通訊,因為它有一個Binder主執行緒,啟動之後就會阻塞等待Client請求,這裡會更新proc->ready_threads,第一次阻塞等待的時候proc->ready_threads=1,之後睡眠。

binder_thread_read(){
  ...
 retry:
    //當前執行緒todo佇列為空且transaction棧為空,則代表該執行緒是空閒的 ,看看是不是自己被複用了
    wait_for_proc_work = thread->transaction_stack == NULL &&
        list_empty(&thread->todo);
 ...//可用執行緒個數+1
    if (wait_for_proc_work)
        proc->ready_threads++; 
    binder_unlock(__func__);
    if (wait_for_proc_work) {
        ...
            //當程序todo佇列沒有資料,則進入休眠等待狀態
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    } else {
        if (non_block) {
            ...
        } else
            //當執行緒todo佇列沒有資料,則進入休眠等待狀態
            ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
    }    
    binder_lock(__func__);
    //被喚醒可用執行緒個數-1
    if (wait_for_proc_work)
        proc->ready_threads--; 
    thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
    ...
    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;

        //先考慮從執行緒todo佇列獲取事務資料
        if (!list_empty(&thread->todo)) {
            w = list_first_entry(&thread->todo, struct binder_work, entry);
        //執行緒todo佇列沒有資料, 則從程序todo對獲取事務資料
        } else if (!list_empty(&proc->todo) && wait_for_proc_work) {
            w = list_first_entry(&proc->todo, struct binder_work, entry);
        } else {
        }
         ..
        if (t->buffer->target_node) {
            cmd = BR_TRANSACTION;  //設定命令為BR_TRANSACTION
        } else {
            cmd = BR_REPLY; //設定命令為BR_REPLY
        }
        .. 
done:
    *consumed = ptr - buffer;
    //建立執行緒的條件
    if (proc->requested_threads + proc->ready_threads == 0 &&
        proc->requested_threads_started < proc->max_threads &&
        (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
         BINDER_LOOPER_STATE_ENTERED))) {
         //需要新建的數目執行緒數+1
        proc->requested_threads++;
        // 生成BR_SPAWN_LOOPER命令,用於建立新的執行緒
        put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer);
    }
    return 0;
}

被Client喚醒後proc->ready_threads會-1,之後變成0,這樣在執行到done的時候,就會發現proc->requested_threads + proc->ready_threads == 0,這是新建Binder執行緒的一個必須條件,再看下其他幾個條件

if (proc->requested_threads + proc->ready_threads == 0 &&
            proc->requested_threads_started < proc->max_threads &&
            (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
             BINDER_LOOPER_STATE_ENTERED)))  
  • proc->requested_threads + proc->ready_threads == 0 :如果目前還沒申請新建Binder執行緒,並且proc->ready_threads空閒Binder執行緒也是0,就需要新建一個Binder執行緒,其實就是為了保證有至少有一個空閒的執行緒
  • proc->requested_threads_started < proc->max_threads:目前啟動的普通Binder執行緒數requested_threads_started還沒達到上限(預設APP程序是15)
  • thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED) 當先執行緒是Binder執行緒,這個是一定滿足的,不知道為什麼列出來

proc->max_threads是多少呢?不同的程序其實設定的是不一樣的,看普通的APP程序,在ProcessState::self()新建ProcessState單利物件的時候會呼叫ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);設定上限,可以看到預設設定的上限是15。

static int open_driver()
{
    int fd = open("/dev/binder", O_RDWR);
    ...
    size_t maxThreads = 15;
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
    ...
}

如果滿足新建的條件,就會將proc->requested_threads加1,並在驅動執行完畢後,利用put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer);通知服務端在使用者空間發起新建Binder執行緒的操作,新建的是普通Binder執行緒,最終再進入binder_thread_write的BC_REGISTER_LOOPER:

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
            void __user *buffer, int size, signed long *consumed)
  {
        ...
        case BC_REGISTER_LOOPER:
              ...
                // requested_threads -- 
                proc->requested_threads--;
                proc->requested_threads_started++;
            }
}

這裡會將proc->requested_threads復原,其實就是-1,並且啟動的Binder執行緒數+1。

個人理解,之所以採用動態新建Binder執行緒的意義有兩點,第一:如果沒有Client請求服務,就保持執行緒數不變,減少資源浪費,需要的時候再分配新執行緒。第二:有請求的情況下,保證至少有一個空閒執行緒是給Client端,以提高Server端響應速度。

Client端執行緒睡眠在哪個佇列上,喚醒Server端哪個等待佇列上的執行緒

先看第一部分:傳送端執行緒睡眠在哪個佇列上?

傳送端執行緒一定睡眠在自己binder_thread的等待佇列上,並且,該佇列上有且只有自己一個睡眠執行緒

再看第二部分:在Binder驅動去喚醒執行緒的時候,喚醒的是哪個等待佇列上的執行緒?

理解這個問題需要理解binder_thread中的 struct binder_transaction * transaction_stack棧,這個棧規定了transaction的執行順序:棧頂的一定先於棧內執行

如果本地操作是BC_REPLY,一定是喚醒之前傳送等待的執行緒,這個是100%的,但是如果是BC_TRANSACTION,那就不一定了,尤其是當兩端互為服務相互請求的時候,場景如下:

  • 程序A的普通執行緒AT1請求B程序的B1服務,喚醒B程序的Binder執行緒,AT1睡眠等待服務結束
  • B程序的B1服務在執行的的時候,需要請求程序A的A1服務,則B程序的Binder執行緒BT1睡眠,喚醒A程序的Binder執行緒,等待服務結束
  • A程序的A1服務在執行的時候,又需要B程序的B2服務,則A程序的binder執行緒AT2睡眠,喚醒B程序的Binder執行緒,等待服務結束

這個時候就會遇到一個問題:喚醒哪個執行緒比較合適?是睡眠在程序佇列上的執行緒,還是之前睡眠的執行緒BT1?答案是:之前睡眠的執行緒BT1,具體看下面的圖解分析

首先第一步A普通執行緒去請求B程序的B1服務,這個時候在A程序的AT1執行緒的binder_ref中會將binder_transaction1入棧,而同樣B的Binder執行緒在讀取binder_work之後,也會將binder_transaction1加入自己的堆疊,如下圖:

binder_transaction堆疊及喚醒那個佇列1

而當B的Binder執行緒被喚醒後,執行Binder實體中的服務時,發現服務函式需要反過來去請求A端的A1服務,那就需要通過Binder向A程序傳送請求,並新建binder_transaction2壓入自己的binder_transaction堆疊,而A程序的Binder執行緒被喚醒後也會將binder_transaction2加入自己的堆疊,會後效果如下:

binder_transaction堆疊及喚醒那個佇列2.jpg

這個時候,還是沒有任何問題,但是恰好在執行A1服務的時候,又需要請求B2服務,這個時候,A1執行緒重複上述壓棧過程,新建binder_transaction3壓入自己的棧,不過在寫入到目標端B的時候,會面臨一個抉擇,寫入那個佇列,是binder_proc上的佇列,還是正在等候A返回的BT1執行緒的佇列?

binder_transaction堆疊及喚醒那個佇列3.jpg

結果已經說過,是BT1的佇列,為什麼呢?因為BT1佇列上的之前的binder_transaction2在等待A程序執行完,但是A端的binder_transaction3同樣要等待binder_transaction3在B程序中執行完畢,也就是說,binder_transaction3在B端一定是先於binder_transaction2執行的,因此喚醒BT1執行緒,並將binder_transaction3壓入BT2的棧,等binder_transaction3執行完畢,出棧後,binder_transaction2才能執行,這樣,既不妨礙binder_transaction2的執行,同樣也能讓睡眠的BT1程序提高利用率,因為最終的堆疊效果就是:

binder_transaction堆疊及喚醒那個佇列4.jpg

而當binder_transaction3完成,出棧的過程其實就簡單了,

  • BT1 執行binder_transaction3,喚醒A端AT2 Binder執行緒,並且BT1繼續睡眠(因為還有等待的transaction)
  • AT2 執行binder_transaction2,喚醒BT1
  • BT1 執行binder_transaction1,喚醒AT1
  • 執行結束

從這裡可以看出,其實設計的還是很巧妙的,讓執行緒複用,提高了效率,還避免了新建不必要的Binder執行緒,在binder驅動中島實現程式碼,其實就是根據binder_transaction中堆疊記錄查詢,
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply)
{..
while (tmp) {
// 找到對方正在等待自己程序的執行緒,如果執行緒沒有在等待自己程序的返回,就不要找了

                    // 判斷是不target_proc中,是不是有執行緒,等待當前執行緒
                    // thread->transaction_stack,這個時候,
                    // 是binder執行緒的,不是普通執行緒 B去請求A服務,
                    // 在A服務的時候,又請求了B,這個時候,A的服務一定要等B處理完,才能再返回B,可以放心用B
                        if (tmp->from && tmp->from->proc == target_proc)
                            target_thread = tmp->from;
                        tmp = tmp->from_parent;
          ...           }
        } }

Binder協議中BC與BR的區別

BC與BR主要是標誌資料及Transaction流向,其中BC是從使用者空間流向核心,而BR是從核心流線使用者空間,比如Client向Server傳送請求的時候,用的是BC_TRANSACTION,當資料被寫入到目標程序後,target_proc所在的程序被喚醒,在核心空間中,會將BC轉換為BR,並將資料與操作傳遞該使用者空間。

BR與BC區別

Binder在傳輸資料的時候是如何層層封裝的–不同層次使用的資料結構(命令的封裝)

核心中,與使用者空間對應的結構體物件都需要新建,但傳輸資料的資料只拷貝一次,就是一次拷貝的時候。

從Client端請求開始分析,暫不考慮java層,只考慮Native,以ServiceManager的addService為例,具體看一下

MediaPlayerService::instantiate();

MediaPlayerService會新建Binder實體,並將其註冊到ServiceManager中:

void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
            String16("media.player"), new MediaPlayerService());
}   

這裡defaultServiceManager其實就是獲取ServiceManager的遠端代理:

sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;

    {
        AutoMutex _l(gDefaultServiceManagerLock);
        if (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
        }
    }

    return gDefaultServiceManager;
}

如果將程式碼簡化其實就是

return gDefaultServiceManager = BpServiceManager (new BpBinder(0));

addService就是呼叫BpServiceManager的addService,

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

這裡會開始第一步的封裝,資料封裝,其實就是講具體的傳輸資料寫入到Parcel物件中,與Parcel對應是ADD_SERVICE_TRANSACTION等具體操作。比較需要注意的就是data.writeStrongBinder,這裡其實就是把Binder實體壓扁:

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

具體做法就是轉換成flat_binder_object,以傳遞Binder的型別、指標之類的資訊:

status_t flatten_binder(const sp<ProcessState>& proc,
    const sp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;

    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        IBinder *local = binder->localBinder();
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE;
            obj.handle = handle;
            obj.cookie = NULL;
        } else {
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = local->getWeakRefs();
            obj.cookie = local;
        }
    } else {
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = NULL;
        obj.cookie = NULL;
    }

    return finish_flatten_binder(binder, obj, out);
}

接下來看 remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); 在上面的環境中,remote()函式返回的就是BpBinder(0),

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

之後通過 IPCThreadState::self()->transact( mHandle, code, data, reply, flags)進行進一步封裝:

status_t IPCThreadState::transact(int32_t handle,
                uint32_t code, const Parcel& data,
                Parcel* reply, uint32_t flags){
    if ((flags & TF_ONE_WAY) == 0) {
        if (err == NO_ERROR) {
            err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
        }
        if (reply) {
            err = waitForResponse(reply);
        } 
        ..
    return err;
    }

writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);是進一步封裝的入口,在這個函式中Parcel& data、handle、code、被進一步封裝成binder_transaction_data物件,並拷貝到mOut的data中去,同時也會將BC_TRANSACTION命令也寫入mOut,這裡與binder_transaction_data對應的CMD是BC_TRANSACTION,binder_transaction_data也儲存了資料的指引新資訊:

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;
    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;
    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } ..
    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));
    return NO_ERROR;
}

mOut封裝結束後,會通過waitForResponse呼叫talkWithDriver繼續封裝:

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    binder_write_read bwr;
    // Is the read buffer empty? 這裡會有同時返回兩個命令的情況 BR_NOOP、BR_COMPLETE
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    // We don't want to write anything if we are still reading
    // from data left in the input buffer and the caller
    // has requested to read the next data.
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    bwr.write_size = outAvail;
    bwr.write_buffer = (long unsigned int)mOut.data();      // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (long unsigned int)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    // Return immediately if there is nothing to do.
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        。。
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
    } while (err == -EINTR);

    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < (ssize_t)mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        return NO_ERROR;
    }
    return err;
}

talkWithDriver會將mOut中的資料與命令繼續封裝成binder_write_read物件,其中bwr.write_buffer就是mOut中的data(binder_transaction_data+BC_TRRANSACTION),之後就會通過ioctl與binder驅動互動,進入核心,這裡與binder_write_read物件對應的CMD是BINDER_WRITE_READ,進入驅動後,是先寫後讀的順序,所以才叫BINDER_WRITE_READ命令,與BINDER_WRITE_READ層級對應的幾個命令碼一般都是跟執行緒、程序、資料整體傳輸相關的操作,不涉及具體的業務處理,比如BINDER_SET_CONTEXT_MGR是將執行緒程式設計ServiceManager執行緒,並建立0號Handle對應的binder_node、BINDER_SET_MAX_THREADS是設定最大的非主Binder執行緒數,而BINDER_WRITE_READ就是表示這是一次讀寫操作:

#define BINDER_CURRENT_PROTOCOL_VERSION 7
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, int64_t)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, int)
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int)
#define BINDER_THREAD_EXIT _IOW('b', 8, int)
#define BINDER_VERSION _IOWR('b', 9, struct binder_version)

詳細看一下binder_ioctl對於BINDER_WRITE_READ的處理,

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    switch (cmd) {
    case BINDER_WRITE_READ: {
        struct binder_write_read bwr;
        ..
        <!--拷貝binder_write_read物件到核心空間-->
        if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
            ret = -EFAULT;
            goto err;
        }
        <!--根據是否需要寫資料處理是不是要寫到目標程序中去-->
        if (bwr.write_size > 0) {
            ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
        }
      <!--根據是否需要寫資料處理是不是要讀,往自己程序裡讀資料-->
        if (bwr.read_size > 0) {
            ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
            <!--是不是要同時喚醒程序上的阻塞佇列-->
            if (!list_empty(&proc->todo))
                wake_up_interruptible(&proc->wait);
        }
        break;
    }
    case BINDER_SET_MAX_THREADS:
        if (copy_from_user(&proc->ma