Android10.0 Binder通信原理(六)-Binder數據如何完成定向打擊

摘要:本節主要來講解Android10.0 Binder的數據是如何完成定向打擊

閱讀本文大約需要花費30分鐘。

文章首發微信公衆號:IngresGe

專注於Android系統級源碼分析,Android的平臺設計,歡迎關注我,謝謝!

[Android取經之路] 的源碼都基於Android-Q(10.0) 進行分析

[Android取經之路] 系列文章:

《系統啓動篇》

  1. Android系統架構
  2. Android是怎麼啓動的
  3. Android 10.0系統啓動之init進程
  4. Android10.0系統啓動之Zygote進程
  5. Android 10.0 系統啓動之SystemServer進程
  6. Android 10.0 系統服務之ActivityMnagerService
  7. Android10.0系統啓動之Launcher(桌面)啓動流程
  8. Android10.0應用進程創建過程以及Zygote的fork流程
  9. Android 10.0 PackageManagerService(一)工作原理及啓動流程
  10. Android 10.0 PackageManagerService(二)權限掃描
  11. Android 10.0 PackageManagerService(三)APK掃描
  12. Android 10.0 PackageManagerService(四)APK安裝流程

《日誌系統篇》

  1. Android10.0 日誌系統分析(一)-logd、logcat 指令說明、分類和屬性
  2. Android10.0 日誌系統分析(二)-logd、logcat架構分析及日誌系統初始化
  3. Android10.0 日誌系統分析(三)-logd、logcat讀寫日誌源碼分析
  4. Android10.0 日誌系統分析(四)-selinux、kernel日誌在logd中的實現​

《Binder通信原理》

  1. Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
  2. Android10.0 Binder通信原理(二)-Binder入門篇
  3. Android10.0 Binder通信原理(三)-ServiceManager篇
  4. Android10.0 Binder通信原理(四)-Native-C\C++實例分析
  5. Android10.0 Binder通信原理(五)-Binder驅動分析
  6. Android10.0 Binder通信原理(六)-Binder數據如何完成定向打擊
  7. Android10.0 Binder通信原理(七)-Framework binder示例
  8. Android10.0 Binder通信原理(八)-Framework層分析
  9. Android10.0 Binder通信原理(九)-AIDL Binder示例
  10. Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub設計模式​​​​​​​
  11. Android10.0 Binder通信原理(十一)-Binder總結

1.概述

前面介紹了Binder驅動的機制、ServiceManager加載的流程、Native層服務註冊、獲取的流程,但是這些都是浮在表面的接口,真正的數據時如何傳輸的?協議碼如何轉向,讓我們這一節深入來分析一下    

 

2.Binder的線程與進程

對於底層Binder驅動,通過binder_procs鏈表記錄所有創建的binder_proc結構體,binder驅動層的每一個binder_proc結構體都與用戶空間的一個用於binder通信的進程一一對應,且每個進程有且只有一個ProcessState對象,這是通過單例模式來保證的。在每個進程中可以有很多個線程,每個線程對應一個IPCThreadState對象,IPCThreadState對象也是單例模式,即一個線程對應一個IPCThreadState對象,在Binder驅動層也有與之相對應的結構,那就是Binder_thread結構體。在binder_proc結構體中通過成員變量rb_root threads,來記錄當前進程內所有的binder_thread。

如下圖所示:

Binder線程池:每個Server進程在啓動時會創建一個binder線程池,並向其中註冊一個Binder線程;之後Server進程也可以向binder線程池註冊新的線程,或者Binder驅動在探測到沒有空閒binder線程時會主動向Server進程註冊新的的binder線程。對於所有Client端進程的binder請求都是交由Server端進程的binder線程來處理的。

 

3 Binder傳輸過程

Binder-IPC機制,就是指在進程間傳輸數據(binder_transaction_data),一次數據的傳輸,稱爲事務(binder_transaction)。

對於多個不同進程向同一個進程發送事務時,這個同一個進程或線程的事務需要串行執行,在Binder驅動中爲binder_proc和binder_thread都有todo隊列。

也就是說對於進程間的通信,就是發送端把binder_transaction節點,插入到目標進程或其子線程的todo隊列中,等目標進程或線程不斷循環地從todo隊列中取出數據並進行相應的操作。

在Binder驅動層,每個接收端進程都有一個todo隊列,用於保存發送端進程發送過來的binder請求,這類請求可以由接收端進程的任意一個空閒的binder線程處理。

接收端進程存在一個或多個binder線程,在每個binder線程裏都有一個todo隊列,也是用於保存發送端進程發送過來的binder請求,這類請求只能由當前binder線程來處理。

binder線程在空閒時進入可中斷的休眠狀態,當自己的todo隊列或所屬進程的todo隊列有新的請求到來時便會喚醒,如果是由所需進程喚醒的,那麼進程會讓其中一個線程處理響應的請求,其他線程再次進入休眠狀態。

 

4 Binder協議的演變

下面展示了Binder協議碼的演變過程,在Android9.0之前,當client向Binder驅動發送BC_TRANSACTION,Binder驅動喚醒Server進程時,會向client進程發送BR_TRANSACTION_COMPLETE,現在這一步被移到了 喚醒Client之後再做,減少了數據延遲。

Android9.0之前的協議碼流程:

Android9.0及之後的協議碼流程:

上面第5步的 BR_TRANSACTION_COMPLETE 被延遲到 第 10步 ,Android做了deferred_thread_work,延遲 TRANSACTION_COMPLETE,因此不會立即返回到用戶空間;這允許目標進程立即開始處理此事務,從而減少延遲。然後,當目標回覆(或出現錯誤)時,我們將返回TRANSACTION_COMPLETE。

 

5.通信流程-binder如何定向打擊

在 《Native-C\C++實例分析》 一節中,我們知道了Native層的服務註冊和獲取流程,我們以服務註冊爲示例來繼續分析

 

5.1 talkWithDriver()

當BpBinder 調用transact()準備進行事務處理,其中發送的語義是BC_TRANSACTION.

IPCThreadState waitForResponse()中啓動了while循環來和binder驅動進行通信,進行事務處理。通過talkWithDriver進行通信.

talkWithDriver()用來不停的和Binder驅動進行通信,ioctl()函數在傳遞BINDER_WRITE_READ語義時,既會使用“輸入buffer”,也會使用“輸出buffer”,所以IPCThreadState專門搞了兩個Parcel類型的成員變量:mIn和mOut。mOut中的內容發出去,發送後的回覆寫進mIn。

 

BINDER_WRITE_READ的命令發給Binder驅動後,ServiceManager有個循環不停的解析Binder驅動的BINDER_WRITE_READ信息,調用binder_parse()進行解析,我們前面addService()時,傳入的code爲ADD_SERVICE_TRANSACTION,在ServiceManager中解析後,對應的值爲SVC_MGR_ADD_SERVICE,最終把服務的name和handle(對象) 存入到svclist中,ServiceManager再通過binder_send_reply()把返回的數據發送出來,在talkWithDriver()中填入mIn中,waitForResponse()進行最終的語義處理,發送出去。


status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }

    binder_write_read bwr;

    ...
    //如果仍在讀取輸入緩衝區中剩餘的數據,並且調用方已請求讀取下一個數據,則不希望寫入任何內容。
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();  //把mOut的數據存入 write_buffer中,

    // This is what we'll read.
    if (doReceive && needRead) {
        //接收數據緩衝區信息的填充。如果以後收到數據,就直接填在mIn中了
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        //沒有收到數據時,把read置空,binder只進行write處理
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    ...
    //當讀緩衝和寫緩衝都爲空,則直接返回
    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 defined(__ANDROID__)
        //通過ioctl不停的讀寫操作,跟Binder Driver進行通信
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
        ...
    } while (err == -EINTR);//當被中斷,則繼續執行


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

    return err;
}

第一次mOut裏面存的是BC_TRANSACTION及binder_transaction_data的值,mIn沒有值,talkWithDriver()中此時,bwr.write_size >0 ; bwr.read_size = 0,調用ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) 和binder驅動進行交互。在Binder驅動一節,我們瞭解到,當write_size>0, read_size=0時,會進入binder_thread_write()

流程轉換:binder_ioctl()->binder_ioctl_write_read()->binder_thread_write()

 

5.2 binder_thread_write

主要是從用戶空間拷貝binder_transaction_data的數據,並傳給binder_transaction()函數進行實際的傳輸。


static int binder_thread_write(struct binder_proc *proc,
            struct binder_thread *thread,
            binder_uintptr_t binder_buffer, size_t size,
            binder_size_t *consumed)
{
    uint32_t cmd;
    struct binder_context *context = proc->context; //原來Service進程的proc的context
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    void __user *ptr = buffer + *consumed;  //buffer的
    void __user *end = buffer + size;

    while (ptr < end && thread->return_error.cmd == BR_OK) {
        int ret;
        //拷貝用戶空間的cmd命令,addService和getService時,爲 BC_TRANSACTION
        if (get_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        trace_binder_command(cmd);
        if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
            atomic_inc(&binder_stats.bc[_IOC_NR(cmd)]);
            atomic_inc(&proc->stats.bc[_IOC_NR(cmd)]);
            atomic_inc(&thread->stats.bc[_IOC_NR(cmd)]);
        }
        switch (cmd) {
        ...
        case BC_TRANSACTION:
        case BC_REPLY: {
            struct binder_transaction_data tr;
            //拷貝用戶空間的 binder_transaction_data ,存入tr
            if (copy_from_user(&tr, ptr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
            //binder事務處理
            binder_transaction(proc, thread, &tr,
                       cmd == BC_REPLY, 0);
            break;
        }
        ...
        default:
            pr_err("%d:%d unknown command %d\n",
                   proc->pid, thread->pid, cmd);
            return -EINVAL;
        }
        *consumed = ptr - buffer;
    }
    return 0;
}

 

5.3 binder_transaction

流程:

  1. 我們向ServiceManager註冊服務時時,Native中 new BpBinder(0,xxx),這裏的handle=0,在binder驅動中找到了目標binder_node--target_node, 指向ServiceManager; 如果此時的handle 不爲0,會根據handle 找到對應的binder_ref節點,最終找到binder_node;
  2. 根據target_node找到目標binder_proc--target_proc;
  3. 創建binder_transaction節點,並將其插入目標進程的todo列表;
  4. 嘗試喚醒目標進程。

 

處理Binder實體與Handle轉化的時候,有下面幾點注意的:

  1. 第一次註冊Binder實體的時候,是向別的進程註冊的,ServiceManager,或者SystemServer中的AMS服務
  2. Client請求服務的時候,一定是由Binder驅動爲Client分配binder_ref,如果本進程的線程請求,fp->type = BINDER_TYPE_BINDER,否則就是fp->type = BINDER_TYPE_HANDLE。
  3. Android中的Parcel裏面的對象一定是flat_binder_object

 

本地是從用戶空間進入binder_transaction(),因此這裏的proc、thread爲前面IPCThreadState 所在進程的內容。


static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply,
                   binder_size_t extra_buffers_size)
{
    int ret;
    struct binder_transaction *t;
    struct binder_work *tcomplete;
    binder_size_t buffer_offset = 0;
    binder_size_t off_start_offset, off_end_offset;
    binder_size_t off_min;
    binder_size_t sg_buf_offset, sg_buf_end_offset;
    struct binder_proc *target_proc = NULL;
    struct binder_thread *target_thread = NULL;
    struct binder_node *target_node = NULL;
    struct binder_transaction *in_reply_to = NULL;
    struct binder_transaction_log_entry *e;
    uint32_t return_error = 0;
    uint32_t return_error_param = 0;
    uint32_t return_error_line = 0;
    binder_size_t last_fixup_obj_off = 0;
    binder_size_t last_fixup_min_off = 0;
    struct binder_context *context = proc->context;
    int t_debug_id = atomic_inc_return(&binder_last_id);
    char *secctx = NULL;
    u32 secctx_sz = 0;
   ...
    //addService第一次進來時,reply爲空
    if (reply) {
        ...
    } else {
        //addService和getService時,傳入的handle=0,表明想訪問ServiceManager
        if (tr->target.handle) {
               //先從tr->target.handle句柄值,找到對應的binder_ref節點,及binder_node節點
            struct binder_ref *ref;
            binder_proc_lock(proc);
            ref = binder_get_ref_olocked(proc, tr->target.handle,
                             true);
            if (ref) {
                target_node = binder_get_node_refs_for_txn(
                        ref->node, &target_proc,
                        &return_error);
            } else {

                return_error = BR_FAILED_REPLY;
            }
            binder_proc_unlock(proc);
        } else {
            mutex_lock(&context->context_mgr_node_lock); //互斥鎖
            // handle=0則找到servicemanager實體
            target_node = context->binder_context_mgr_node;
            if (target_node)
                //獲取ServiceManager進程的target_proc
                target_node = binder_get_node_refs_for_txn(
                        target_node, &target_proc,
                        &return_error);
            else
                return_error = BR_DEAD_REPLY;
            mutex_unlock(&context->context_mgr_node_lock); //釋放鎖
            if (target_node && target_proc == proc) {
                binder_user_error("%d:%d got transaction to context manager from process owning it\n",
                          proc->pid, thread->pid);
                return_error = BR_FAILED_REPLY;
                return_error_param = -EINVAL;
                return_error_line = __LINE__;
                goto err_invalid_target_handle;
            }
        }
            ...
        //檢查Client進程是否有權限向Server進程發送請求
        if (security_binder_transaction(proc->tsk,
                        target_proc->tsk) < 0) {
            return_error = BR_FAILED_REPLY;
            return_error_param = -EPERM;
            return_error_line = __LINE__;
            goto err_invalid_target_handle;
        }
        binder_inner_proc_lock(proc);
        //如果flag不是oneway,並且現場的transaction_stack存在內容
        if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
            struct binder_transaction *tmp;
            tmp = thread->transaction_stack;
            if (tmp->to_thread != thread) {
                spin_lock(&tmp->lock);
                binder_user_error("%d:%d got new transaction with bad transaction stack, transaction %d has target %d:%d\n",
                    proc->pid, thread->pid, tmp->debug_id,
                    tmp->to_proc ? tmp->to_proc->pid : 0,
                    tmp->to_thread ?
                    tmp->to_thread->pid : 0);
                spin_unlock(&tmp->lock);
                binder_inner_proc_unlock(proc);
                return_error = BR_FAILED_REPLY;
                return_error_param = -EPROTO;
                return_error_line = __LINE__;
                goto err_bad_call_stack;
            }
            while (tmp) {
                struct binder_thread *from;

                spin_lock(&tmp->lock);
                from = tmp->from;
                if (from && from->proc == target_proc) {
                    atomic_inc(&from->tmp_ref);
                    target_thread = from;
                    spin_unlock(&tmp->lock);
                    break;
                }
                spin_unlock(&tmp->lock);
                tmp = tmp->from_parent;
            }
        }
        binder_inner_proc_unlock(proc);
    }
    if (target_thread)
        e->to_thread = target_thread->pid;
    e->to_proc = target_proc->pid;

    // 創建binder_transaction節點
    t = kzalloc(sizeof(*t), GFP_KERNEL); 
    if (t == NULL) {
        return_error = BR_FAILED_REPLY;
        return_error_param = -ENOMEM;
        return_error_line = __LINE__;
        goto err_alloc_t_failed;
    }
    binder_stats_created(BINDER_STAT_TRANSACTION);
    spin_lock_init(&t->lock);
    //創建一個binder_work節點
    tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
        ...
    binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);

    t->debug_id = t_debug_id;
    ...

    if (!reply && !(tr->flags & TF_ONE_WAY))
        t->from = thread;   // 返回proc的當前線程
    else
        t->from = NULL;
    t->sender_euid = task_euid(proc->tsk);  // 源線程用戶id
    t->to_proc = target_proc;               // 負責處理該事務的進程--ServiceManager
    t->to_thread = target_thread;           // 負責處理該事務的線程
        // 將binder_transaction_data的code、flags域記入binder_transaction節點
    t->code = tr->code;                     // ADD_SERVICE_TRANSACTION
    t->flags = tr->flags;                   // TF_ACCEPT_FDS
    // 源線程優先級設置
    if (!(t->flags & TF_ONE_WAY) &&
        binder_supported_policy(current->policy)) {
        /* Inherit supported policies for synchronous transactions */
        t->priority.sched_policy = current->policy;
        t->priority.prio = current->normal_prio;
    } else {
        /* Otherwise, fall back to the default priority */
        t->priority = target_proc->default_priority;
    }

    if (target_node && target_node->txn_security_ctx) {
        u32 secid;
        size_t added_size;

        security_task_getsecid(proc->tsk, &secid);
        ret = security_secid_to_secctx(secid, &secctx, &secctx_sz);
        if (ret) {
            return_error = BR_FAILED_REPLY;
            return_error_param = ret;
            return_error_line = __LINE__;
            goto err_get_secctx_failed;
        }
        added_size = ALIGN(secctx_sz, sizeof(u64));
        extra_buffers_size += added_size;
        if (extra_buffers_size < added_size) {
            /* integer overflow of extra_buffers_size */
            return_error = BR_FAILED_REPLY;
            return_error_param = EINVAL;
            return_error_line = __LINE__;
            goto err_bad_extra_size;
        }
    }

    //分配一塊buffer用於保存mOut中的data和mObjects 中的offset數據
    t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
        tr->offsets_size, extra_buffers_size,
        !reply && (t->flags & TF_ONE_WAY));
        ...
    if (secctx) {
        size_t buf_offset = ALIGN(tr->data_size, sizeof(void *)) +
                    ALIGN(tr->offsets_size, sizeof(void *)) +
                    ALIGN(extra_buffers_size, sizeof(void *)) -
                    ALIGN(secctx_sz, sizeof(u64));

        t->security_ctx = (uintptr_t)t->buffer->user_data + buf_offset;
        binder_alloc_copy_to_buffer(&target_proc->alloc,
                        t->buffer, buf_offset,
                        secctx, secctx_sz);
        security_release_secctx(secctx, secctx_sz);
        secctx = NULL;
    }
    t->buffer->debug_id = t->debug_id;
    t->buffer->transaction = t;             // 緩衝區正交給哪個事務使用
    t->buffer->target_node = target_node;   // 緩衝區正交給哪個Binder實體對象使用
    trace_binder_transaction_alloc_buf(t->buffer);

    //拷貝用戶空間的binder_transaction_data中ptr.buffer到內核,存入t->buffer
    //從mOut中將data拷貝到buffer中
    if (binder_alloc_copy_user_to_buffer(
                &target_proc->alloc,
                t->buffer, 0,
                (const void __user *)
                    (uintptr_t)tr->data.ptr.buffer,
                tr->data_size)) {
               ...
    }
    //拷貝用戶空間的binder_transaction_data中ptr.offsets到內核,存入t->buffer
    //從mObjects 中將offset數據拷貝到buffer中data之後
    if (binder_alloc_copy_user_to_buffer(
                &target_proc->alloc,
                t->buffer,
                ALIGN(tr->data_size, sizeof(void *)),
                (const void __user *)
                    (uintptr_t)tr->data.ptr.offsets,
                tr->offsets_size)) {
               ...
    }
    ...
    off_start_offset = ALIGN(tr->data_size, sizeof(void *));
    buffer_offset = off_start_offset;
    off_end_offset = off_start_offset + tr->offsets_size;
    sg_buf_offset = ALIGN(off_end_offset, sizeof(void *));
    sg_buf_end_offset = sg_buf_offset + extra_buffers_size -
        ALIGN(secctx_sz, sizeof(u64));
    off_min = 0;
    

    //從data中解析出所有的binder實體併爲其創建binder_node和binder_ref
    for (buffer_offset = off_start_offset; buffer_offset < off_end_offset;
         buffer_offset += sizeof(binder_size_t)) {
        struct binder_object_header *hdr;
        size_t object_size;
        struct binder_object object;
        binder_size_t object_offset;

        //得到object_offset的大小
        binder_alloc_copy_from_buffer(&target_proc->alloc,
                          &object_offset,
                          t->buffer,
                          buffer_offset,
                          sizeof(object_offset));
        //從t->buffer中解析object,object_size=sizeof(flat_binder_object)
        object_size = binder_get_object(target_proc, t->buffer,
                        object_offset, &object);
        if (object_size == 0 || object_offset < off_min) {
            ...
            return_error = BR_FAILED_REPLY;
            return_error_param = -EINVAL;
            return_error_line = __LINE__;
            goto err_bad_offset;
        }

        hdr = &object.hdr;
        off_min = object_offset + object_size;
        //根據addService時,writeStrongBinder()寫的是實體Binder--BINDER_TYPE_BINDER,還是代理Binder--BINDER_TYPE_HANDLE類型來決定
        switch (hdr->type) {
        case BINDER_TYPE_BINDER:
        case BINDER_TYPE_WEAK_BINDER: {
            // 如果是binder實體
            struct flat_binder_object *fp;

            fp = to_flat_binder_object(hdr);
            //在目標進程的binder_proc中創建對應的binder_ref紅黑樹節點
            //BINDER_TYPE_BINDER 轉成BINDER_TYPE_HANDLE
            //爲binder實體創建binder_node並添加到紅黑樹proc->nodes.rb_node中
            ret = binder_translate_binder(fp, t, thread);
            if (ret < 0) {
                return_error = BR_FAILED_REPLY;
                return_error_param = ret;
                return_error_line = __LINE__;
                goto err_translate_failed;
            }
            binder_alloc_copy_to_buffer(&target_proc->alloc,
                            t->buffer, object_offset,
                            fp, sizeof(*fp));
        } break;
        case BINDER_TYPE_HANDLE:
        case BINDER_TYPE_WEAK_HANDLE: {
            // 如果是 Binder引用,拿到Binder的handle
            struct flat_binder_object *fp;

            fp = to_flat_binder_object(hdr);
             //在遠端進程的binder_proc中找到一個binder_ref紅黑樹節點
            //解析出引用的flat_binder_object,並在請求proc下創建服務的引用
            ret = binder_translate_handle(fp, t, thread);
            if (ret < 0) {
                return_error = BR_FAILED_REPLY;
                return_error_param = ret;
                return_error_line = __LINE__;
                goto err_translate_failed;
            }
            binder_alloc_copy_to_buffer(&target_proc->alloc,
                            t->buffer, object_offset,
                            fp, sizeof(*fp));
        } break;
            ...
        }
    }
    //transaction事務處理完成
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    t->work.type = BINDER_WORK_TRANSACTION; //設置事務類型

    if (reply) {
    ... 
    } else if (!(t->flags & TF_ONE_WAY)) {
        BUG_ON(t->buffer->async_transaction != 0);
        binder_inner_proc_lock(proc);
        //延遲 TRANSACTION_COMPLETE,因此我們不會立即返回到用戶空間;
        //這允許目標進程立即開始處理此事務,從而減少延遲。
        //然後,當目標回覆(或出現錯誤)時,我們將返回TRANSACTION_COMPLETE。
        //把binder_transaction節點插入target_list(即目標todo隊列)
        binder_enqueue_deferred_thread_work_ilocked(thread, tcomplete);
        t->need_reply = 1;  //是雙向的所以需要回復
        t->from_parent = thread->transaction_stack;
        thread->transaction_stack = t;  //將傳遞數據保存在請求線程的中,以後後續緩存釋放等
        binder_inner_proc_unlock(proc);
        //將數據放到目標進程的todo list中去,喚醒目標進程處理請求,這裏喚醒ServiceManager進行處理
        if (!binder_proc_transaction(t, target_proc, target_thread)) {
            binder_inner_proc_lock(proc);
            binder_pop_transaction_ilocked(thread, t);
            binder_inner_proc_unlock(proc);
            goto err_dead_proc_or_thread;
        }
    } else {
        ...
    }
    if (target_thread)
        binder_thread_dec_tmpref(target_thread);
    binder_proc_dec_tmpref(target_proc);
    if (target_node)
        binder_dec_node_tmpref(target_node);
    smp_wmb();
    WRITE_ONCE(e->debug_id_done, t_debug_id);
    return;

    ...
}

整理了一張binder_transaction的示意圖:

Client的數據傳來後,進行事務處理後,喚醒Server端,Server端進行數據讀取。假設我們這裏的Server端爲ServiceManager,在ServiceManager中有一個循環,不停的向Binder驅動發送讀寫的信息,讀到內容後調用binder_parse()進行解析。

binder_loop()中請求時,write_size=0,到binder驅動中會進入binder_thread_read

 

5.3 binder_thread_read

         由於本次是通過Servicemanager進入了binder_thread_read(),因此,這裏的proc,thread都是ServiceManager的內容。

當目標進程ServiceManager被喚醒時,會接着執行自己的binder_thread_read(),嘗試解析並執行那些剛收來的工作。

 

流程:

  1. 利用wait_event_xxxx()讓自己掛起,等待下一次被喚醒;
  2. 喚醒後找到合適的待處理的工作節點,即binder_transaction節點;
  3. 把binder_transaction中的信息整理到一個binder_transaction_data中;
  4. 整理一個cmd整數值,具體數值或者爲BR_TRANSACTION,或者爲BR_REPLY;
  5. 將cmd數值和binder_transaction_data拷貝到用戶態;
  6. 如有必要,將得到的binder_transaction節點插入目標端線程的transaction_stack堆棧中。
static int binder_thread_read(struct binder_proc *proc,
                  struct binder_thread *thread,
                  binder_uintptr_t binder_buffer, size_t size,
                  binder_size_t *consumed, int non_block)
{
    ...
       retry:
       //優先考慮thread節點的todo鏈表中有沒有工作需要完成
        wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
        ...
    if (wait_for_proc_work) {
        if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
            wait_event_interruptible(binder_user_error_wait,
                         binder_stop_on_user_error < 2);
        }
        binder_restore_priority(current, proc->default_priority);
    }
    if (non_block) {
        if (!binder_has_work(thread, wait_for_proc_work))
            ret = -EAGAIN;
    } else {
         //休眠在這裏, wait_for_proc_work爲false
        ret = binder_wait_for_work(thread, wait_for_proc_work);
    }
    while (1) {
        ...
        switch (w->type) {
        case BINDER_WORK_TRANSACTION: {
            binder_inner_proc_unlock(proc);
            t = container_of(w, struct binder_transaction, work);
        } break;
        }
        ...
        //數據在內核中封裝成結構體binder_transaction傳輸,
        //據返回到應用層之後要封裝成結構體 binder_transaction_data,所以這裏就是將數據從新封裝成binder_transaction_data返回給用戶空間
        if (t->buffer->target_node) {
            struct binder_node *target_node = t->buffer->target_node;
            struct binder_priority node_prio;

            trd->target.ptr = target_node->ptr;
                  // 用目標binder_node中記錄的cookie值給binder_transaction_data的cookie域賦值
                      // 這個值就是目標binder實體的地址
            trd->cookie =  target_node->cookie;
            node_prio.sched_policy = target_node->sched_policy;
            node_prio.prio = target_node->min_priority;
            binder_transaction_priority(current, t, node_prio,
                            target_node->inherit_rt);
            cmd = BR_TRANSACTION;
        } else {
            trd->target.ptr = 0;
            trd->cookie = 0;
            cmd = BR_REPLY;
        }
        trd->code = t->code;
        trd->flags = t->flags;
        trd->sender_euid = from_kuid(current_user_ns(), t->sender_euid);

        t_from = binder_get_txn_from(t);
        if (t_from) {
            struct task_struct *sender = t_from->proc->tsk;

            trd->sender_pid =
                task_tgid_nr_ns(sender,
                        task_active_pid_ns(current));
        } else {
            trd->sender_pid = 0;
        }

        trd->data_size = t->buffer->data_size;
        trd->offsets_size = t->buffer->offsets_size;
        trd->data.ptr.buffer = (uintptr_t)t->buffer->user_data; //獲取buffer在用戶空間中的地址
        trd->data.ptr.offsets = trd->data.ptr.buffer +
                    ALIGN(t->buffer->data_size,
                        sizeof(void *));

        tr.secctx = t->security_ctx;
        if (t->security_ctx) {
            cmd = BR_TRANSACTION_SEC_CTX;
            trsize = sizeof(tr);
        }
        //將cmd命令寫入用戶態,此時應該是BR_TRANSACTION
        if (put_user(cmd, (uint32_t __user *)ptr)) {
            if (t_from)
                binder_thread_dec_tmpref(t_from);

            binder_cleanup_transaction(t, "put_user failed",
                           BR_FAILED_REPLY);

            return -EFAULT;
        }
        ptr += sizeof(uint32_t);
        
        //將重新封裝後的數據拷貝到用戶空間
        if (copy_to_user(ptr, &tr, trsize)) {
            if (t_from)
                binder_thread_dec_tmpref(t_from);

            binder_cleanup_transaction(t, "copy_to_user failed",
                           BR_FAILED_REPLY);

            return -EFAULT;
        }
        ptr += trsize;
    }
        if (t_from)
            binder_thread_dec_tmpref(t_from);
        t->buffer->allow_user_free = 1;
        if (cmd != BR_REPLY && !(t->flags & TF_ONE_WAY)) {
                 //binder_transaction節點插入了目標線程的transaction_stack堆棧,而且是以to_thread域來連接堆棧中的其他節點
            binder_inner_proc_lock(thread->proc);
            t->to_parent = thread->transaction_stack;
            t->to_thread = thread;
            thread->transaction_stack = t;
            binder_inner_proc_unlock(thread->proc);
        } else {
                 // TF_ONE_WAY情況,此時會刪除binder_transaction節點
            binder_free_transaction(t);
        }
        break;
}

如果沒有工作需要做,binder_thread_read()函數就進入睡眠或返回,否則binder_thread_read()函數會從todo隊列摘下了一個節點,並把節點裏的數據整理成一個binder_transaction_data結構,然後通過copy_to_user()把該結構傳到用戶態。

 

5.4 目標端事務處理

5.4.1.當ServiceManager爲Server端時

  1. 當Binder驅動把 BR_TRANSACTION 放入用戶空間後,其實此時ServiceManager爲Server端,那麼進入ServiceManager,binder_parse()解析BR_TRANSACTION 調用svcmgr_handler處理,code=SVC_MGR_ADD_SERVICE, 然後調用binder_send_reply() cmd_reply=BC_REPLY,cmd_free=BC_FREE_BUFFER, 發給Binder驅動,其中write_size > 0
  2. Binder驅動讀取ServiceManager傳來的數據,進入binder_transaction(),cmd=BC_REPLY,喚醒Client進程,把BR_TRANSACTION_COMPLETE分別發給ServiceManager 和Client進程
  3. IPCThreadState 收到BR_TRANSACTION_COMPLETE後,把數據寫入mIn,mOut移除內容,繼續調用talkWithDriver(),向Binder驅動發起BINDER_WRITE_READ請求,此時mOut無值,mIn有內容
  4. Binder驅動進入binder_thread_read(),根據ServiceManager發來的reply數據,發送BR_REPLY給client
  5. IPCThreadState waitForResponse()收到BR_REPLY後,釋放內存空間

 

5.4.2 當ProcessState中的服務爲Server端時

  1. 當Binder驅動把 BR_TRANSACTION 放入用戶空間後,waitForResponse()讀到CMD=BR_TRANSACTION,會調用executeCommand()進行處理,最終調用到BBinder的transact(),最終會調到onTransact()
  2. 在調用完transact()動作後,executeCommand()會判斷tr.flags有沒有攜帶TF_ONE_WAY標記,如果沒有攜帶,說明這次傳輸是需要回復的,於是調用sendReply()進行回覆

 

6.總結:

6.1 服務註冊

服務註冊過程(addService)核心功能:在服務所在進程創建binder_node,在servicemanager進程創建binder_ref。其中binder_ref的desc在同一個進程內是唯一的:

  1. 每個進程binder_proc所記錄的binder_ref的handle值是從1開始遞增的;
  2. 所有進程binder_proc所記錄的handle=0的binder_ref都指向service manager;
  3. 同一個服務的binder_node在不同進程的binder_ref的handle值可以不同;

 

   在《Native-C\C++實例分析》一節,我們說到了 MediaPlayerService 向ServiceManager 進行服務註冊的流程,其中 MediaPlayerService是Client,ServiceManager是Server進程,通信流程圖如下所示:

過程分析:

  1. MediaPlayerService進程調用ioctl()向Binder驅動發送IPC數據,該過程可以理解成一個事務binder_transaction(記爲T1),執行當前操作的線程binder_thread(記爲thread1),則T1>from_parent=NULL,T1>from=thread1,thread1>transaction_stack=T1。
  2. Binder驅動收到該Binder請求,生成BR_TRANSACTION命令,選擇目標處理該請求的線程,即ServiceManager的binder線程(記爲thread2),則 T1>to_parent = NULL,T1>to_thread = thread2。並將整個binder_transaction數據(記爲T2)插入到目標線程的todo隊列;
  3. ServiceManager不斷地從todo鏈表檢查是否有節點,如果有的話就會摘取下來並解析、執行,ServiceManager的線程thread2收到T2後,調用服務註冊函數將服務”media.player”註冊到服務目錄中。當服務註冊完成後,生成IPC應答數據(BC_REPLY),T2>form_parent = T1,T2>from = thread2, thread2>transaction_stack = T2。
  4. Binder驅動收到該Binder應答請求,生成BR_REPLY命令,T2>to_parent = T1,T2>to_thread = thread1, thread1>transaction_stack = T2。在MediaPlayerService收到該命令後,知道服務註冊完成便可以正常使用。

整個過程中,BC_TRANSACTION和BR_TRANSACTION過程是一個完整的事務過程;BC_REPLY和BR_REPLY是一個完整的事務過程。到此,其他進行便可以獲取該服務,使用服務提供的方法。

 

6.2獲取服務

獲取服務(getService)過程,就是向servicemanager進程查詢指定服務,當執行binder_transaction()時,會區分請求服務所屬進程情況:

  1. 當請求服務的進程與服務屬於不同進程,則爲請求服務所在進程創建binder_ref對象,指向服務進程中的binder_node;最終readStrongBinder(),返回的是BpBinder對象
  2. 當請求服務的進程與服務屬於同一進程,則不再創建新對象,只是引用計數加1,並且修改type爲BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER。最終readStrongBinder(),返回的是BBinder對象的真實子類

至此,Binder的定向打擊,IPC數據流轉就介紹完成了,下一節我們一起來看看Binder在Framework層是如何工作的。

 

我的微信公衆號:IngresGe

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章