Binder 对象引用计数

作者: ColdWave | 来源:发表于2018-07-02 18:41 被阅读0次

Binder 对象引用计数技术

微信截图_20180702183953.png 微信截图_20180702184020.png

它们的交互过程可以分为5个步骤,如下所示:

(1) 运行在 Client 进程的 BpBinder 通过 Binder Drvier 向运行在 Server 进程的 BBinder 发送通信请求,Binder Driver 根据 Client 进程的 BpBinder 的句柄值找到对应的 binder_ref

(2) Binder Dirver 根据找到的 binder_ref 找到 binder_node, 并创建 binder_transaction(事务) 来描述此次通信过程.

(3) Binder Driver 根据 binder_node 找到 Server 进程的 BBinder, 将 Client 进程传递的通信数据发送给它.

(4) BBinder 处理完 Client 的请求后,将通信结果返回给 Binder Driver, Binder Driver 找到之前创建的 binder_transaction.

(5) Binder Driver 根据前面找到的事务的相关属性来找到发出进程通信请求的 Client 进程,并且通知 Client 进程将通信结果返回给对应的 BpBinder 来处理.

从上述过程来看, BpBinder 依赖于 binder_ref, 而 BBinder 依赖于 binder_node.所以必须采用一种技术保证不能销毁一个还被依赖着的对象.

BBinder 的生命周期

BBinder 可能会被 Server 进程的其他对象引用,也可能被 Binder Driver 的 binder_node 引用.

  • 被 Server 进程的其他对象引用:
    RefBase ---- 通过智能指针控制其生命周期.
  • 被 binder_node 引用
    binder_node 处于 Kernel Space,无法使用 智能指针.
    需要重新约定一套规则来维护它们的引用计数.
  • BR_INCREFS
  • BR_ACQUIRE
  • BR_DECREFS
  • BR_RELEASE

维护 Service 组件 即 BBinder 的生命周期.

Binder Driver 要和目标进程或线程通信时,会把一个工作项加入到它的 todo list 中.目标进程或线程会不断的调用 Driver 的 binder_thread_read 来检查它的 todo list 中是否有新的工作项.如果有,将之读取出来,然后返回到用户空间去处理.

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)
{
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    int ret = 0;
    int wait_for_proc_work;

    ......

    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w = NULL;
        struct list_head *list = NULL;
        struct binder_transaction *t = NULL;
        struct binder_thread *t_from;

        binder_inner_proc_lock(proc);
        if (!binder_worklist_empty_ilocked(&thread->todo))
            list = &thread->todo;
        else if (!binder_worklist_empty_ilocked(&proc->todo) &&
               wait_for_proc_work)
            list = &proc->todo;
        else {
            binder_inner_proc_unlock(proc);

            /* no data added */
            if (ptr - buffer == 4 && !thread->looper_need_return)
                goto retry;
            break;
        }

        if (end - ptr < sizeof(tr) + 4) {
            binder_inner_proc_unlock(proc);
            break;
        }
        w = binder_dequeue_work_head_ilocked(list);

        switch (w->type) {
        ....
        case BINDER_WORK_NODE: {
            // 此时 binder_node 是一个 工作项
            struct binder_node *node = container_of(w, struct binder_node, work);
            int strong, weak;
            // 指向 Userspace 的 Service 组件内部的一个引用计数对象(weakref_impl)的地址(用户空间地址)
            // 指向 Userspace 的 Service 组件的地址(用户空间地址)
            binder_uintptr_t node_ptr = node->ptr;
            binder_uintptr_t node_cookie = node->cookie;
            int node_debug_id = node->debug_id;
            int has_weak_ref;
            int has_strong_ref;
            void __user *orig_ptr = ptr;

            BUG_ON(proc != node->proc);
            // 这是什么意思?
            strong = node->internal_strong_refs ||
                    node->local_strong_refs;
            weak = !hlist_empty(&node->refs) ||
                    node->local_weak_refs ||
                    node->tmp_refs || strong;

            /*
             * has_strong_ref 和 has_weak_ref (默认为1):
             * 如果 Driver 已经请求了 Server 进程的 BBinder 为 binder_node 增加了引用计数,
             * 那么 Driver 就会将 binder_node 的 has_strong_ref 和 has_weak_ref 设置为1; 否则为0.
            */
            has_strong_ref = node->has_strong_ref;
            has_weak_ref = node->has_weak_ref;

/**
* 引用技术的说明:
* 1. internal_strong_refs, local_strong_refs 描述 binder_node 的强引用计数。
* 2. local_weak_refs 描述 binder_node 的弱引用计数。
* 3. 一个 binder_node 请求 Service 执行某一操作时,会增加该 Service 的 strong refs 或 weak refs;
*    相应的,binder_node 会将成员变量 has_strong_ref 或 has_weak_ref 的值设置为1.
* 4. 当一个 Service 组件完成一个 binder_node 所请求的操作后,
*    binder_node 就会请求减少该 Service 的 strong refs 或 weak refs。
* 5. binder_node 在请求一个 Service 增加或减少引用计数的过程中,
*    会将成员变量 pending_strong_ref 或 pending_weak_ref 的值设置为1;
*    当 Service 增加或减少了引用计数后,binder_node 就会将这两个成员变量的值设置为0.
* 6. 当 binder_node 的引用计数由0->1, 或由1->0时,Binder Driver 会请求相应的 Service 增加或减少其引用计数。
*    这时候Binder Driver会将修改引用计数的操作封装成一个类型为 binder_node 的工作项,
*    即将 binder_node 的 work 的值设置为 BINDER_WORK_NODE,并将它添加到相应的进程的 todo list 中,等待处理。
*/

            /*
             * 这里有一个思想上的转换: 
             * has_weak_ref, local_weak_refs, has_strong_ref, local_strong_refs 
             * 描述的是 binder_node 的引用计数.而不是 Service 的引用计数.
             * 只是 binder_node 和 Service 相关,当 binder_node 引用计数变化时,需要通知 Service 修改引用计数.
            */

            if (weak && !has_weak_ref) {
                node->has_weak_ref = 1;
                node->pending_weak_ref = 1;
                node->local_weak_refs++;
            }
            if (strong && !has_strong_ref) {
                node->has_strong_ref = 1;
                node->pending_strong_ref = 1;
                node->local_strong_refs++;
            }
            if (!strong && has_strong_ref)
                node->has_strong_ref = 0;
            if (!weak && has_weak_ref)
                node->has_weak_ref = 0;

            /*
             * binder_node 已经引用了一个 BBinder,但是没有增加它的弱引用计数,通过 BR_INCREFS 增加弱引用计数.
            */
            if (weak && !has_weak_ref)
                ret = binder_put_node_cmd(
                        proc, thread, &ptr, node_ptr,
                        node_cookie, node_debug_id,
                        BR_INCREFS, "BR_INCREFS");
            /*
             * binder_node 已经引用了一个 BBinder,但是还没有增加它的强引用计数,通过 BR_ACQUIRE 增加 强引用计数.
            */
            if (!ret && strong && !has_strong_ref)
                ret = binder_put_node_cmd(
                        proc, thread, &ptr, node_ptr,
                        node_cookie, node_debug_id,
                        BR_ACQUIRE, "BR_ACQUIRE");
            /*
             * binder_node 不再引用 BBinder, 但是还没有减少强引用计数,通过 BR_RELEASE 减少强引用计数.
            */
            if (!ret && !strong && has_strong_ref)
                ret = binder_put_node_cmd(
                        proc, thread, &ptr, node_ptr,
                        node_cookie, node_debug_id,
                        BR_RELEASE, "BR_RELEASE");
            /*
             * binder_node 不再引用 BBinder,但是还没有减少它的弱引用计数,通过 BR_DECREFS 减少弱引用计数.
            */
            if (!ret && !weak && has_weak_ref)
                ret = binder_put_node_cmd(
                        proc, thread, &ptr, node_ptr,
                        node_cookie, node_debug_id,
                        BR_DECREFS, "BR_DECREFS");
            if (ret)
                return ret;
        } break;
        ...
        }
    }
}
  • UserSpace 如何处理?
// IPCThreadState
status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;
    RefBase::weakref_type* refs;
    status_t result = NO_ERROR;

    switch ((uint32_t)cmd) {

    case BR_ACQUIRE:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();
        obj->incStrong(mProcess.get());  // 增加 强引用计数.

        mOut.writeInt32(BC_ACQUIRE_DONE);   // 告诉 Driver , 增加强引用计数 操作 DONE
        mOut.writePointer((uintptr_t)refs);
        mOut.writePointer((uintptr_t)obj);
        break;

    case BR_RELEASE:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();
        mPendingStrongDerefs.push(obj);    // 放到 vector 中,等下次 IO 命令处理
        break;

    case BR_INCREFS:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();
        refs->incWeak(mProcess.get());       // 增加 弱引用计数
        mOut.writeInt32(BC_INCREFS_DONE);    // 发送 BC_INCREFS_DONE 命令.
        mOut.writePointer((uintptr_t)refs);
        mOut.writePointer((uintptr_t)obj);
        break;

    case BR_DECREFS:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();
        mPendingWeakDerefs.push(refs);      // 放到 vector 中,等下次 IO 命令处理
        break;
    }
}
/*
 * 减少引用计数无返回,且不着急处理.
 * 增加引用计数有返回,且必须马上处理,因为如果不处理,很有可能对象被释放.
*/

binder_node 生命周期

binder_node 由 Driver 创建,并且被 binder_ref 所引用.

当 Client 进程通过 ServiceManager 获取 Service 组件的代理对象接口时,Drvier就会找到该 Service 组件的 binder_node, 然后再创建一个 binder_ref 来引用它.这是就需要增加 binder_node 的引用计数.相反,当 Client 不再引用一个 Service 组件时,就会请求 Driver 释放 binder_ref,这是就需要减少 binder_node 的引用计数.

  • 增加引用计数: binder_inc_node
static int binder_inc_node(struct binder_node *node, int strong, int internal,
               struct list_head *target_list)
{
    int ret;

    binder_node_inner_lock(node);
    ret = binder_inc_node_nilocked(node, strong, internal, target_list);
    binder_node_inner_unlock(node);

    return ret;
}
  • BpBinder <---> binder_ref

  • BBinder <---> binder_node

  • binder_ref --引用--> binder_node

  • binder_node 对应于 Server, 那么它的 local 的意思就是 Server 进程内; 那么 internal 就是 描述 binder_ref 对 binder_node 的引用,可以认为是 kernel internal.

  • local_weak_refs, local_strong_refs 描述 binder_node 和 BBinder 之间的引用关系.如 Drvier 请求 Server 进程指向某一操作,就会增加 BBinder 对应的 binder_node 的 local 引用计数,避免 binder_node 被过早的销毁.

  • Client 进程通过 binder_ref 来引用 binder_node 时, Driver 就会增加 binder_node 的 internal 引用计数,防止 binder_node 的过早销毁.

  • 为什么只有 internal_strong_refs ,没有 internal_weak_refs?

    因为 binder_node 的成员变量 refs 隐含了 internal_weak_refs.

/*
 * strong : 是否是强引用计数.
 * internal: 内部引用计数还是外部引用计数.
 * target_list: 指向目标进程或目标线程的 todo list.如果非NULL,就表示增加了 binder_node 引用计数后,需要增加它所引用的 BBinder 引用计数.
*/
static int binder_inc_node_nilocked(struct binder_node *node, int strong,
                    int internal,
                    struct list_head *target_list)
{
    struct binder_proc *proc = node->proc;

    assert_spin_locked(&node->lock);
    if (proc)
        assert_spin_locked(&proc->inner_lock);
    if (strong) {
        if (internal) {
            if (target_list == NULL &&
                node->internal_strong_refs == 0 &&
                !(node->proc &&
                  node == node->proc->context->
                      binder_context_mgr_node &&
                  node->has_strong_ref)) {
                pr_err("invalid inc strong node for %d\n",
                    node->debug_id);
                return -EINVAL;
            }
            node->internal_strong_refs++;
        } else
            node->local_strong_refs++;

        /*
         * 一个 binder_node 请求 Service 执行某一操作时,会增加该 Service 的 strong refs 或 weak refs;
         * 相应的,binder_node 会将成员变量 has_strong_ref 或 has_weak_ref 的值设置为1.
         */
        if (!node->has_strong_ref && target_list) {
            binder_dequeue_work_ilocked(&node->work);
            binder_enqueue_work_ilocked(&node->work, target_list);
        }
    } else {
        if (!internal)
            node->local_weak_refs++;
        if (!node->has_weak_ref && list_empty(&node->work.entry)) {
            if (target_list == NULL) {
                pr_err("invalid inc weak node for %d\n",
                    node->debug_id);
                return -EINVAL;
            }
            binder_enqueue_work_ilocked(&node->work, target_list);
        }
    }
    return 0;
}
  • 减少引用计数: binder_dec_node
static void binder_dec_node(struct binder_node *node, int strong, int internal)
{
    bool free_node;

    binder_node_inner_lock(node);
    free_node = binder_dec_node_nilocked(node, strong, internal);
    binder_node_inner_unlock(node);
    if (free_node)
        binder_free_node(node);
}
static bool binder_dec_node_nilocked(struct binder_node *node,
                     int strong, int internal)
{
    struct binder_proc *proc = node->proc;

    assert_spin_locked(&node->lock);
    if (proc)
        assert_spin_locked(&proc->inner_lock);
    if (strong) {
        if (internal)
            node->internal_strong_refs--;
        else
            node->local_strong_refs--;
        if (node->local_strong_refs || node->internal_strong_refs)
            return false;
    } else {
        if (!internal)
            node->local_weak_refs--;
        if (node->local_weak_refs || node->tmp_refs ||
                !hlist_empty(&node->refs))
            return false;
    }

    if (proc && (node->has_strong_ref || node->has_weak_ref)) {
        if (list_empty(&node->work.entry)) {
            binder_enqueue_work_ilocked(&node->work, &proc->todo);
            binder_wakeup_proc_ilocked(proc);
        }
    } else {
        if (hlist_empty(&node->refs) && !node->local_strong_refs &&
            !node->local_weak_refs && !node->tmp_refs) {
            if (proc) {
                binder_dequeue_work_ilocked(&node->work);
                rb_erase(&node->rb_node, &proc->nodes);
                binder_debug(BINDER_DEBUG_INTERNAL_REFS,
                         "refless node %d deleted\n",
                         node->debug_id);
            } else {
                BUG_ON(!list_empty(&node->work.entry));
                spin_lock(&binder_dead_nodes_lock);
                /*
                 * tmp_refs could have changed so
                 * check it again
                 */
                if (node->tmp_refs) {
                    spin_unlock(&binder_dead_nodes_lock);
                    return false;
                }
                hlist_del(&node->dead_node);
                spin_unlock(&binder_dead_nodes_lock);
                binder_debug(BINDER_DEBUG_INTERNAL_REFS,
                         "dead node %d deleted\n",
                         node->debug_id);
            }
            return true;
        }
    }
    return false;
}

binder_ref 生命周期

  • BC_ACQUIRE
  • BC_INCREFS
  • BC_RELEASE
  • BC_DECREFS

增加或减少 binder_ref 的引用计数.

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;
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    while (ptr < end && thread->return_error.cmd == BR_OK) {
        int ret;

        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_INCREFS:
        case BC_ACQUIRE:
        case BC_RELEASE:
        case BC_DECREFS: {
            uint32_t target;
            const char *debug_string;
            bool strong = cmd == BC_ACQUIRE || cmd == BC_RELEASE;     // 是否为 强引用计数
            bool increment = cmd == BC_INCREFS || cmd == BC_ACQUIRE;  // 是否为 增加引用计数
            struct binder_ref_data rdata;

            if (get_user(target, (uint32_t __user *)ptr))
                return -EFAULT;

            ptr += sizeof(uint32_t);
            ret = -1;
            if (increment && !target) {
                struct binder_node *ctx_mgr_node;
                mutex_lock(&context->context_mgr_node_lock);
                ctx_mgr_node = context->binder_context_mgr_node;   // 是否为 ServiceManager 的 引用对象
                if (ctx_mgr_node)
                    ret = binder_inc_ref_for_node(
                            proc, ctx_mgr_node,
                            strong, NULL, &rdata);
                mutex_unlock(&context->context_mgr_node_lock);
            }
            if (ret)
                ret = binder_update_ref_for_handle(
                        proc, target, increment, strong,
                        &rdata);
            break;
        }
        }
    }
}
  • binder_inc_ref_for_node
/**
 * binder_inc_ref_for_node() - increment the ref for given proc/node
 * @proc:    proc containing the ref
 * @node:    target node
 * @strong:  true=strong reference, false=weak reference
 * @target_list: worklist to use if node is incremented
 * @rdata:   the id/refcount data for the ref
 *
 * Given a proc and node, increment the ref. Create the ref if it
 * doesn't already exist
 *
 * Return: 0 if successful, else errno
 */
static int binder_inc_ref_for_node(struct binder_proc *proc,
            struct binder_node *node,
            bool strong,
            struct list_head *target_list,
            struct binder_ref_data *rdata)
{
    struct binder_ref *ref;
    struct binder_ref *new_ref = NULL;
    int ret = 0;

    binder_proc_lock(proc);
    ref = binder_get_ref_for_node_olocked(proc, node, NULL);
    if (!ref) {
        binder_proc_unlock(proc);
        new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
        if (!new_ref)
            return -ENOMEM;
        binder_proc_lock(proc);
        ref = binder_get_ref_for_node_olocked(proc, node, new_ref);
    }
    ret = binder_inc_ref_olocked(ref, strong, target_list);
    *rdata = ref->data;
    binder_proc_unlock(proc);
    if (new_ref && ref != new_ref)
        /*
         * Another thread created the ref first so
         * free the one we allocated
         */
        kfree(new_ref);
    return ret;
}
  • binder_get_ref_for_node_olocked: 获取 binder_node 对应的 binder_ref
/**
 * binder_get_ref_for_node_olocked() - get the ref associated with given node
 * @proc:   binder_proc that owns the ref
 * @node:   binder_node of target
 * @new_ref:    newly allocated binder_ref to be initialized or %NULL
 *
 * Look up the ref for the given node and return it if it exists
 *
 * If it doesn't exist and the caller provides a newly allocated
 * ref, initialize the fields of the newly allocated ref and insert
 * into the given proc rb_trees and node refs list.
 *
 * Return:  the ref for node. It is possible that another thread
 *      allocated/initialized the ref first in which case the
 *      returned ref would be different than the passed-in
 *      new_ref. new_ref must be kfree'd by the caller in
 *      this case.
 */
static struct binder_ref *binder_get_ref_for_node_olocked(
                    struct binder_proc *proc,
                    struct binder_node *node,
                    struct binder_ref *new_ref)
{
    struct binder_context *context = proc->context;
    struct rb_node **p = &proc->refs_by_node.rb_node;
    struct rb_node *parent = NULL;
    struct binder_ref *ref;
    struct rb_node *n;

    // 遍历 binder_proc->refs_by_node 的红黑树, 关键字是 binder_node 的地址.查找引用该 binder_node 的 binder_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;
    }
    if (!new_ref)
        return NULL;

    binder_stats_created(BINDER_STAT_REF);
    /*
     * new_ref 的初始化
    */
    new_ref->data.debug_id = atomic_inc_return(&binder_last_id);
    new_ref->proc = proc;
    new_ref->node = node;
    rb_link_node(&new_ref->rb_node_node, parent, p);
    rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);

    // 如果是 ServiceManager 的 binder_ref, desc = 0, 否则 desc = 1
    new_ref->data.desc = (node == context->binder_context_mgr_node) ? 0 : 1;
    // 根据已存在的 desc, 重新设置 new_ref 的 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->data.desc > new_ref->data.desc)
            break;
        new_ref->data.desc = ref->data.desc + 1; // rfs_by_desc 根据 desc 的大小排序的红黑树.
    }

    // 插入 binder_ref 的 rfs_by_desc 的 红黑树中.
    p = &proc->refs_by_desc.rb_node;
    while (*p) {
        parent = *p;
        ref = rb_entry(parent, struct binder_ref, rb_node_desc);

        if (new_ref->data.desc < ref->data.desc)
            p = &(*p)->rb_left;
        else if (new_ref->data.desc > ref->data.desc)
            p = &(*p)->rb_right;
        else
            BUG();
    }
    rb_link_node(&new_ref->rb_node_desc, parent, p);
    rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc);

    binder_node_lock(node);
    // 将 new_ref 插入到 它所引用的 binder_node 的 hlist 中.
    hlist_add_head(&new_ref->node_entry, &node->refs);

    binder_debug(BINDER_DEBUG_INTERNAL_REFS,
             "%d new ref %d desc %d for node %d\n",
              proc->pid, new_ref->data.debug_id, new_ref->data.desc,
              node->debug_id);
    binder_node_unlock(node);
    return new_ref;
}
/**
 * binder_inc_ref_olocked() - increment the ref for given handle
 * @ref:         ref to be incremented
 * @strong:      if true, strong increment, else weak
 * @target_list: list to queue node work on
 *
 * Increment the ref. @ref->proc->outer_lock must be held on entry
 *
 * Return: 0, if successful, else errno
 */
 /*
  * target_list: 表示 增加了 binder_ref 后,需要增加 它所引用的 binder_node 或 BBinder 的引用计数.
 */
static int binder_inc_ref_olocked(struct binder_ref *ref, int strong,
                  struct list_head *target_list)
{
    int ret;

    if (strong) {
        /*
         * 1. 强引用计数:
         * 2. ref->data.strong == 0 
         * 3. 说明此时 binder_ref 对应的 binder_node 的 internal_strong_refs 需要 +1
        */
        if (ref->data.strong == 0) {
            ret = binder_inc_node(ref->node, 1, 1, target_list);
            if (ret)
                return ret;
        }
        ref->data.strong++;
    } else {
        if (ref->data.weak == 0) {
            ret = binder_inc_node(ref->node, 0, 1, target_list);
            if (ret)
                return ret;
        }
        ref->data.weak++;
    }
    return 0;
}
  • binder_update_ref_for_handle
/**
 * binder_update_ref_for_handle() - inc/dec the ref for given handle
 * @proc:   proc containing the ref
 * @desc:   the handle associated with the ref
 * @increment:  true=inc reference, false=dec reference
 * @strong: true=strong reference, false=weak reference
 * @rdata:  the id/refcount data for the ref
 *
 * Given a proc and ref handle, increment or decrement the ref
 * according to "increment" arg.
 *
 * Return: 0 if successful, else errno
 */
static int binder_update_ref_for_handle(struct binder_proc *proc,
        uint32_t desc, bool increment, bool strong,
        struct binder_ref_data *rdata)
{
    int ret = 0;
    struct binder_ref *ref;
    bool delete_ref = false;

    binder_proc_lock(proc);
    ref = binder_get_ref_olocked(proc, desc, strong);
    
    if (increment)
        ret = binder_inc_ref_olocked(ref, strong, NULL);
    else
        delete_ref = binder_dec_ref_olocked(ref, strong);

    if (rdata)
        *rdata = ref->data;
    binder_proc_unlock(proc);

    if (delete_ref)
        binder_free_ref(ref);  // 释放 binder_ref,如果 binder_ref->node 不为 NULL,释放 binder_ref->node
    return ret;

err_no_ref:
    binder_proc_unlock(proc);
    return ret;
}
/**
 * binder_dec_ref() - dec the ref for given handle
 * @ref:    ref to be decremented
 * @strong: if true, strong decrement, else weak
 *
 * Decrement the ref.
 *
 * Return: true if ref is cleaned up and ready to be freed
 */
static bool binder_dec_ref_olocked(struct binder_ref *ref, int strong)
{
    if (strong) {
        ref->data.strong--;
        if (ref->data.strong == 0)
            binder_dec_node(ref->node, strong, 1);  // 如果 强引用计数==0, 那么减少 binder_ref 所引用的 binder_node 的 internal 引用计数.
    } else {
        ref->data.weak--;
    }
    if (ref->data.strong == 0 && ref->data.weak == 0) {
        binder_cleanup_ref_olocked(ref);
        return true;
    }
    return false;
}
static void binder_cleanup_ref_olocked(struct binder_ref *ref)
{
    bool delete_node = false;

    rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc);
    rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node);

    binder_node_inner_lock(ref->node);
    if (ref->data.strong)
        binder_dec_node_nilocked(ref->node, 1, 1);

    hlist_del(&ref->node_entry);
    // 减少 binder_ref 所引用的 binder_node 的 strong | internal 引用计数,然后确认是否删除 binder_node
    delete_node = binder_dec_node_nilocked(ref->node, 0, 1);
    binder_node_inner_unlock(ref->node);
    /*
     * Clear ref->node unless we want the caller to free the node
     */
    if (!delete_node) {
        /*
         * The caller uses ref->node to determine
         * whether the node needs to be freed. Clear
         * it since the node is still alive.
         */
        ref->node = NULL;
    }

    // 删除 binder_ref 的死亡通知
    if (ref->death) {
        binder_dequeue_work(ref->proc, &ref->death->work);
        binder_stats_deleted(BINDER_STAT_DEATH);
    }
    binder_stats_deleted(BINDER_STAT_REF);
}

BpBinder 生命周期

  • BpBinder 一方面会被 Client 进程中的其他对象引用,另一方面也会引用 binder_ref.
  • Client 通过 BC_ACQUIRE, BC_INCREFS, BC_RELEASE, BC_DECREFS 来引用 binder_ref
  • BpBinder 和 binder_ref 通过 句柄值 desc 进行关联.Client 通过 desc 来维护所有的 BpBinder.
class ProcessState {
            struct handle_entry {
                IBinder* binder;
                RefBase::weakref_type* refs;
            };

            handle_entry*       lookupHandleLocked(int32_t handle);

            Vector<handle_entry>mHandleToObject;
};

ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
    const size_t N=mHandleToObject.size();
    if (N <= (size_t)handle) {
        handle_entry e;
        e.binder = NULL;
        e.refs = NULL;
        status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
        if (err < NO_ERROR) return NULL;
    }
    return &mHandleToObject.editItemAt(handle);
}

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {  // ServiceManager
                // Special case for context manager...
                // The context manager is the only object for which we create
                // a BpBinder proxy without already holding a reference.
                // Perform a dummy transaction to ensure the context manager
                // is registered before we create the first local reference
                // to it (which will occur when creating the BpBinder).
                // If a local reference is created for the BpBinder when the
                // context manager is not present, the driver will fail to
                // provide a reference to the context manager, but the
                // driver API does not return status.
                //
                // Note that this is not race-free if the context manager
                // dies while this code runs.
                //
                // TODO: add a driver API to wait for context manager, or
                // stop special casing handle 0 for context manager and add
                // a driver API to get a handle to the context manager with
                // proper reference counting.

                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }

            b = new BpBinder(handle); 
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}
void IPCThreadState::incStrongHandle(int32_t handle)
{
    LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle);
    mOut.writeInt32(BC_ACQUIRE);
    mOut.writeInt32(handle);
}

void IPCThreadState::decStrongHandle(int32_t handle)
{
    LOG_REMOTEREFS("IPCThreadState::decStrongHandle(%d)\n", handle);
    mOut.writeInt32(BC_RELEASE);
    mOut.writeInt32(handle);
}

void IPCThreadState::incWeakHandle(int32_t handle)
{
    LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle);
    mOut.writeInt32(BC_INCREFS);
    mOut.writeInt32(handle);
}

void IPCThreadState::decWeakHandle(int32_t handle)
{
    LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle);
    mOut.writeInt32(BC_DECREFS);
    mOut.writeInt32(handle);
}
BpBinder::BpBinder(int32_t handle)
    : mHandle(handle)
    , mAlive(1)
    , mObitsSent(0)
    , mObituaries(NULL)
{
    ALOGV("Creating BpBinder %p handle %d\n", this, mHandle);

    extendObjectLifetime(OBJECT_LIFETIME_WEAK);
    IPCThreadState::self()->incWeakHandle(handle);
}

BpBinder::~BpBinder()
{
    ALOGV("Destroying BpBinder %p handle %d\n", this, mHandle);

    IPCThreadState* ipc = IPCThreadState::self();

    mLock.lock();
    Vector<Obituary>* obits = mObituaries;
    if(obits != NULL) {
        if (ipc) ipc->clearDeathNotification(mHandle, this);
        mObituaries = NULL;
    }
    mLock.unlock();

    if (obits != NULL) {
        // XXX Should we tell any remaining DeathRecipient
        // objects that the last strong ref has gone away, so they
        // are no longer linked?
        delete obits;
    }

    if (ipc) {
        ipc->expungeHandle(mHandle, this);
        ipc->decWeakHandle(mHandle);
    }
}
  • 强引用计数

    为了减少 Client 进程 和 Driver 交互开销,只有第一个被强指针引用时,Client进程才会请求Driver增加 binder_ref 的引用计数.反之.

当一个对象第一次被强指针引用时,它的成员函数 onFirstRef 会被调用;当一个对象不再被强指针引用时,它的成员函数 onLastStrongRef 就会被调用.

void BpBinder::onFirstRef()
{
    ALOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle);
    IPCThreadState* ipc = IPCThreadState::self();
    if (ipc) ipc->incStrongHandle(mHandle);
}

void BpBinder::onLastStrongRef(const void* /*id*/)
{
    ALOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle);
    IF_ALOGV() {
        printRefs();
    }
    IPCThreadState* ipc = IPCThreadState::self();
    if (ipc) ipc->decStrongHandle(mHandle);
}

相关文章

  • Binder 对象引用计数

    Binder 对象引用计数技术 它们的交互过程可以分为5个步骤,如下所示: (1) 运行在 Client 进程的 ...

  • iOS内存管理初探 – 引用计数、AutoRelease与ARC

    引用计数式内存管理 引用计数 iOS通过引用计数管理对象的生命周期,每个对象有其引用计数。 对象被强引用时引用计数...

  • Golang GC

    常见垃圾回收机制 引用计数 对每个对象维护一个引用计数,当引用对象的对象被销毁时,引用计数-1,如果引用计数 为0...

  • GC算法

    引用计数 每个对象有一个引用计数,当对象被多引用一次,引用计数加一,当引用被释放,引用计数减一,当引用计数为零,则...

  • Java中GC回收算法

    ①引用计数算法:针对每一个对象,保存一个对该对象引用的计数,该对象引用增加,引用计数相应的增加;引用失效时,引用计...

  • 基于引用计数的内存管理

    引用计数原则 对象的初始引用计数是1。 当引用被创建或者拷贝,引用计数加1。 当对象的引用被销毁或者重新赋值,对象...

  • GO语言垃圾回收机制

    常见的垃圾回收方法: 引用计数:对每个对象维护一个引用计数,当引用该对象的对象被销毁时,引用计数减1,当引用计数器...

  • GC算法 垃圾收集器

    一、对象存活判断 引用计数:每个对象都有引用计数属性,新增一个引用时计数+1,引用释放时计数-1,当计数为0时可以...

  • 垃圾收集器与内存分配策略

    一、判断对象回收1、判断方法引用计数算法:对象每次引用,引用计数加1,取消引用减一,当引用计数为0,则判断对象可回...

  • python基础之内存管理

    一、对象的引用计数机制 1、定义:Python内部使用引用计数,来保持追踪内存中的对象,所有对象都有引用计数 2、...

网友评论

    本文标题:Binder 对象引用计数

    本文链接:https://www.haomeiwen.com/subject/acoluftx.html