Android 重学系列 Binder 死亡代理

背景

这是Binder系列的最后一篇了。让我们来聊聊Binder的死亡代理是怎么处理。我们之前只是聊了Binder的启动和传输数据,还差最后一个模块就补上整个缺口了。
如果遇到问题:https://www.jianshu.com/p/e22005e5c411

正文

AMS 为app注册死亡接受通知

还记得在第一篇的调用AppThread在初始化的时候会初始化,调用attachApplicationLocked方法,为AppThread绑定一个ApplicationTread的binder对象。实际上在这个过程是携带ApplicationTread对象,通过Binder通信到了ActivityManagerService(简称AMS),并让AMS远程绑定App的行为。
文件:/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

 private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid, int callingUid, long startSeq) {

....
        final String processName = app.processName;
        try {
            AppDeathRecipient adr = new AppDeathRecipient(
                    app, pid, thread);
            thread.asBinder().linkToDeath(adr, 0);
            app.deathRecipient = adr;
        } catch (RemoteException e) {
            app.resetPackageList(mProcessStats);
            startProcessLocked(app, "link fail", processName);
            return false;
        }

....
        return true;
    }

这里有一个新的类AppDeathRecipient,让我们看看究竟

   private final class AppDeathRecipient implements IBinder.DeathRecipient {
        final ProcessRecord mApp;
        final int mPid;
        final IApplicationThread mAppThread;

        AppDeathRecipient(ProcessRecord app, int pid,
                IApplicationThread thread) {

            mApp = app;
            mPid = pid;
            mAppThread = thread;
        }

        @Override
        public void binderDied() {

            synchronized(ActivityManagerService.this) {
                appDiedLocked(mApp, mPid, mAppThread, true);
            }
        }
    }

文件:/frameworks/base/core/java/android/os/IBinder.java

可以看到这个AppDeathRecipient是继承于IBinder.DeathRecipient。

public interface DeathRecipient {
        public void binderDied();
    }

这个接口意味着当binder死亡回调之后的行为。

细节补充

文件:/frameworks/native/libs/binder/Parcel.cpp

那么,我们趁热打铁,接着上一章,我们把BBinder本地binder传输到binder中。我们必定会经过binder驱动的binder_transaction这个步骤:

case BINDER_TYPE_BINDER:
        case BINDER_TYPE_WEAK_BINDER: {
....

            if (fp->type == BINDER_TYPE_BINDER)
                fp->type = BINDER_TYPE_HANDLE;
            else
                fp->type = BINDER_TYPE_WEAK_HANDLE;
            fp->handle = ref->desc;
...
        } break;

可以看到的是,此时我们会把在Parcel中设置好的BINDER_TYPE_BINDER,转化为BINDER_TYPE_HANDLE一个handle的类型。因此,我们可以看到此时当AMS执行attachApplicationLocked方法的时候,必定会从Parcel读取里面的binder对象(readStrongBinder方法)。我们看看Parcel的方法。
文件:

status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const
{
    return unflatten_binder(ProcessState::self(), *this, val);
}

status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, wp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);

    if (flat) {
        switch (flat->hdr.type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_WEAK_BINDER:
                if (flat->binder != 0) {
                    out->set_object_and_refs(
                        reinterpret_cast<IBinder*>(flat->cookie),
                        reinterpret_cast<RefBase::weakref_type*>(flat->binder));
                } else {
                    *out = NULL;
                }
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
            case BINDER_TYPE_WEAK_HANDLE:
                *out = proc->getWeakProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->unsafe_get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

因此此时我们会根据传下来的type,当时BINDER_TYPE_HANDLE的type时候,就转型为BpBinder,否则代表是本地对象在本地读取直接取出cookie设置进数据中。

因此我们可以推导出,此时IApplicationThread thread是一个BpBinder对象,对应着Java层的BinderProxy对象。

linkToDeath

文件:/frameworks/base/core/java/android/os/Binder.java

public class Binder implements IBinder {
    public void linkToDeath(DeathRecipient recipient, int flags) {
    }

    public boolean unlinkToDeath(DeathRecipient recipient, int flags) {
        return true;
    }
}

final class BinderProxy implements IBinder {
    public native void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException;
    public native boolean unlinkToDeath(DeathRecipient recipient, int flags);
}

把整个Binder缩减下来就能知道,实际上Binder本身虽然存在这两个方法,但是由于是本地的类,因此应该由本地处理其死亡,而Proxy身为代理类会通过这种手段告诉binder他要死亡。是否是这样的,我们看看BinderProxy源码就知道了。

native层linkToDeath

文件:/frameworks/base/core/jni/android_util_Binder.cpp

static void android_os_BinderProxy_linkToDeath(JNIEnv* env, jobject obj,
        jobject recipient, jint flags) // throws RemoteException
{
...

    BinderProxyNativeData *nd = getBPNativeData(env, obj);
    IBinder* target = nd->mObject.get();

    if (!target->localBinder()) {
        DeathRecipientList* list = nd->mOrgue.get();
        sp<JavaDeathRecipient> jdr = new JavaDeathRecipient(env, recipient, list);
        status_t err = target->linkToDeath(jdr, NULL, flags);
        if (err != NO_ERROR) {
            jdr->clearReference();
            signalExceptionForError(env, obj, err, true /*canThrowRemoteException*/);
        }
    }
}

实际上这里做的事情很简单,就是去除BinderProxy中BinderProxyNativeData 中的JavaDeathRecipient对象,调用BpBinder的linkToDeath方法。

BpBinder的linkToDeath

文件:/frameworks/native/libs/binder/BpBinder.cpp

status_t BpBinder::linkToDeath(
    const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags)
{
    Obituary ob;
    ob.recipient = recipient;
    ob.cookie = cookie;
    ob.flags = flags;

    LOG_ALWAYS_FATAL_IF(recipient == NULL,
                        "linkToDeath(): recipient must be non-NULL");

    {
        AutoMutex _l(mLock);

        if (!mObitsSent) {
            if (!mObituaries) {
                mObituaries = new Vector<Obituary>;
                if (!mObituaries) {
                    return NO_MEMORY;
                }
                getWeakRefs()->incWeak(this);
                IPCThreadState* self = IPCThreadState::self();
                self->requestDeathNotification(mHandle, this);
                self->flushCommands();
            }
            ssize_t res = mObituaries->add(ob);
            return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res;
        }
    }

    return DEAD_OBJECT;
}
  • 调用IPCThreadState的requestDeathNotification方法 写入死亡通知方法
  • 调用IPCThreadState的flushCommands 发送死亡通知
  • 把DeathRecipient,cookie(此时为NULL),flags添加到mObituaries这个Vector向量中。

requestDeathNotification与flushCommands

status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy)
{
    mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION);
    mOut.writeInt32((int32_t)handle);
    mOut.writePointer((uintptr_t)proxy);
    return NO_ERROR;
}

void IPCThreadState::flushCommands()
{
    if (mProcess->mDriverFD <= 0)
        return;
    talkWithDriver(false);
    // The flush could have caused post-write refcount decrements to have
    // been executed, which in turn could result in BC_RELEASE/BC_DECREFS
    // being queued in mOut. So flush again, if we need to.
    if (mOut.dataSize() > 0) {
        talkWithDriver(false);
    }
...
}

从上面两个代码段我们可以知道此时IPCThread往binder驱动中写入BC_REQUEST_DEATH_NOTIFICATION命令,并且写入handle(代表BpBinder的句柄),proxy(代表当前BpBinder指针),再通过ioctl传输到Binder驱动

Binder驱动binder_transaction

冗余的东西不在聊,我们直接看看这个方法的分支片段。

//处理死亡通知
case BC_REQUEST_DEATH_NOTIFICATION:
        case BC_CLEAR_DEATH_NOTIFICATION: {
            uint32_t target;
            binder_uintptr_t cookie;
            struct binder_ref *ref;
            struct binder_ref_death *death;
//设置从用户空间下来的数据
            if (get_user(target, (uint32_t __user *)ptr))//handle 句柄
                return -EFAULT;
            ptr += sizeof(uint32_t);
            if (get_user(cookie, (binder_uintptr_t __user *)ptr))//BpBinder
                return -EFAULT;
            ptr += sizeof(binder_uintptr_t);
            ref = binder_get_ref(proc, target);
            ...
//处理死亡通知
            if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
                ...
                death = kzalloc(sizeof(*death), GFP_KERNEL);
                ...
                binder_stats_created(BINDER_STAT_DEATH);
                INIT_LIST_HEAD(&death->work.entry);
                death->cookie = cookie;
                ref->death = death;
                if (ref->node->proc == NULL) {
                    ref->death->work.type = BINDER_WORK_DEAD_BINDER;
                    if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
                        list_add_tail(&ref->death->work.entry, &thread->todo);
                    } else {
                        list_add_tail(&ref->death->work.entry, &proc->todo);
                        wake_up_interruptible(&proc->wait);
                    }
                }
            } else {
                ...
            }
        } break;
  • 1.首先先通过句柄获取远程端对应的binder引用
  • 2.创建并且初始化一个binder_death对象(包含死亡链表以及BpBinder),并且把对应的binder引用设置上这个对象。接着初始化好death->work.entry这个散列链表的头。

还有一种特殊情况需要处理,当目标进程已经死亡,设置标记为BINDER_WORK_DEAD_BINDER。换到这个场景就是指还没来及删除binder驱动下方的binder对象,远端进程的App死亡了。

这里分为两种情况讨论:

  • binder_looper的标志位打开BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED这两个时候,此时会放到当前进程的binder_thread 的todo中。

  • binder_looper的标志位没打开BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED这两个时候,则直接加到当前进程的进程todo(空闲工作列表中)中,并且立刻唤起当前进程(当前情景是指AMS),详情见:对端(AMS)被唤醒,读取数据 小节。

好像到这里就没有事情要做了。实际上确实如此,这个命令仅仅标记了要死亡的binder引用是哪个。并没有做更多的事情,但是却让java层的回调跟住了binder引用。

当目标死亡时候(App进程死亡)

当App进程死亡的时候,才是真正的开始开始资源回收,因此我们能对应着看看看当App进程死亡,开始回收资源时候,把binder驱动文件close的场景。
这里就不继续跟踪linux内核到binder驱动。
文件:/drivers/staging/android/binder.c

static const struct file_operations binder_fops = {
    .owner = THIS_MODULE,
    .poll = binder_poll,
    .unlocked_ioctl = binder_ioctl,
    .compat_ioctl = binder_ioctl,
    .mmap = binder_mmap,
    .open = binder_open,
    .flush = binder_flush,
    .release = binder_release,
};

我们能看到的时候,当资源关闭就会调用binder_release方法。

static int binder_release(struct inode *nodp, struct file *filp)
{
    struct binder_proc *proc = filp->private_data;

    debugfs_remove(proc->debugfs_entry);
    binder_defer_work(proc, BINDER_DEFERRED_RELEASE);
    return 0;
}

static DECLARE_WORK(binder_deferred_work, binder_deferred_func);

static void
binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer)
{
    mutex_lock(&binder_deferred_lock);
    proc->deferred_work |= defer;
    if (hlist_unhashed(&proc->deferred_work_node)) {
        hlist_add_head(&proc->deferred_work_node,
                &binder_deferred_list);
        queue_work(binder_deferred_workqueue, &binder_deferred_work);
    }
    mutex_unlock(&binder_deferred_lock);
}

可以看到的是,这里的处理当执行多个close资源回收的方法,此时会依次进入到binder_deferred_workqueue这个哈希链表(这个数据结构和hashmap很像,之后会在算法部分解析以下)。调用的是binder_deferred_work指向的binder_deferred_func方法。

static void binder_deferred_func(struct work_struct *work)
{
    struct binder_proc *proc;
    struct files_struct *files;

    int defer;

    do {
        binder_lock(__func__);
        mutex_lock(&binder_deferred_lock);
        if (!hlist_empty(&binder_deferred_list)) {
            proc = hlist_entry(binder_deferred_list.first,
                    struct binder_proc, deferred_work_node);
            hlist_del_init(&proc->deferred_work_node);
            defer = proc->deferred_work;
            proc->deferred_work = 0;
        } else {
            proc = NULL;
            defer = 0;
        }
        mutex_unlock(&binder_deferred_lock);

        files = NULL;
        if (defer & BINDER_DEFERRED_PUT_FILES) {
            files = proc->files;
            if (files)
                proc->files = NULL;
        }

        if (defer & BINDER_DEFERRED_FLUSH)
            binder_deferred_flush(proc);

        if (defer & BINDER_DEFERRED_RELEASE)
            binder_deferred_release(proc); /* frees proc */

        binder_unlock(__func__);
        if (files)
            put_files_struct(files);
    } while (proc);
}

此时我们可以看到,这里进入到了一个循环。不断的取出binder_deferred_list中删除任务,直到这个队列中已经没有任何东西,则把binder_proc指向null,跳出循环。值得注意的是,此时传进来的标志位为BINDER_DEFERRED_RELEASE,因此我们会执行核心方法binder_deferred_release。

App进程清除binder驱动中的数据(binder_deferred_release)

真正开始删除binder驱动中依附于binder_proc的数据将会在binder_deferred_release中清除

static void binder_deferred_release(struct binder_proc *proc)
{
    struct binder_transaction *t;
    struct rb_node *n;
    int threads, nodes, incoming_refs, outgoing_refs, buffers,
        active_transactions, page_count;

    BUG_ON(proc->vma);
    BUG_ON(proc->files);

    hlist_del(&proc->proc_node);
// 当前进程为binder_context_mgr_node清除
    if (binder_context_mgr_node && binder_context_mgr_node->proc == proc) {
        binder_debug(BINDER_DEBUG_DEAD_BINDER,
                 "%s: %d context_mgr_node gone\n",
                 __func__, proc->pid);
        binder_context_mgr_node = NULL;
    }
//释放binder_proc的binder_thread中数据
    threads = 0;
    active_transactions = 0;
    while ((n = rb_first(&proc->threads))) {
        struct binder_thread *thread;

        thread = rb_entry(n, struct binder_thread, rb_node);
        threads++;
        active_transactions += binder_free_thread(proc, thread);
    }
//释放binder_proc的nodes中数据
    nodes = 0;
    incoming_refs = 0;
    while ((n = rb_first(&proc->nodes))) {
        struct binder_node *node;

        node = rb_entry(n, struct binder_node, rb_node);
        nodes++;
        rb_erase(&node->rb_node, &proc->nodes);
        incoming_refs = binder_node_release(node, incoming_refs);
    }
//释放binder_proc的refs_by_desc中数据
    outgoing_refs = 0;
    while ((n = rb_first(&proc->refs_by_desc))) {
        struct binder_ref *ref;

        ref = rb_entry(n, struct binder_ref, rb_node_desc);
        outgoing_refs++;
        binder_delete_ref(ref);
    }

    binder_release_work(&proc->todo);
    binder_release_work(&proc->delivered_death);
//释放正在使用的allocated_buffers数据
    buffers = 0;
    while ((n = rb_first(&proc->allocated_buffers))) {
        struct binder_buffer *buffer;

        buffer = rb_entry(n, struct binder_buffer, rb_node);

        t = buffer->transaction;
        if (t) {
            t->buffer = NULL;
            buffer->transaction = NULL;
            pr_err("release proc %d, transaction %d, not freed\n",
                   proc->pid, t->debug_id);
            /*BUG();*/
        }

        binder_free_buf(proc, buffer);
        buffers++;
    }

    binder_stats_deleted(BINDER_STAT_PROC);
//释放映射地址
    page_count = 0;
    if (proc->pages) {
        int i;

        for (i = 0; i < proc->buffer_size / PAGE_SIZE; i++) {
            void *page_addr;

            if (!proc->pages[i])
                continue;

            page_addr = proc->buffer + i * PAGE_SIZE;
            binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
                     "%s: %d: page %d at %p not freed\n",
                     __func__, proc->pid, i, page_addr);
            unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
            __free_page(proc->pages[i]);
            page_count++;
        }
        kfree(proc->pages);
        vfree(proc->buffer);
    }

    put_task_struct(proc->tsk);

    binder_debug(BINDER_DEBUG_OPEN_CLOSE,
             "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d, buffers %d, pages %d\n",
             __func__, proc->pid, threads, nodes, incoming_refs,
             outgoing_refs, active_transactions, buffers, page_count);
//释放binder_proc
    kfree(proc);
}

释放数据分为7个步骤。

  • 1.加入当前是binder_context_mgr_node对应的进程则清除这个全局binder_node (binder实体对象)。这个对象就是指service_manager进程对应的binder_node

  • 2.释放binder_proc的binder_thread链表中数据。这个链表管理所有的binder_thread,这个binder对象会为每一个新的进程想要通过ioctl和新的binder进程通信,都会新建一个。当然使用poll的时候也会新建一个。而这在binder_thread中又有todo 工作链表需要清理,因此会调用binder_free_thread清理里面的工作。这里选择的方案是,把所有的binder工作任务一一都传递给各个需要这个工作的对端,这样就相当于清理了工作链表和依赖栈。

  • 3.释放binder_proc的nodes链表中数据。这个链表管理着所有经过binder_proc当前进程通过传输的binder对象的实体,因此会也需要清理。

  • 4.释放binder_proc的refs_by_desc中数据。这个是代表着binder_proc所有实体的引用,我们会通过引用去找到对应的实体,因此我们需要先清理binder_node实体,再清理引用。

  • 5.释放正在使用的allocated_buffers数据。因为在清理binder_thread中已经处理了对应的工作,但是一般allocated_buffer是等到对端回应之后再清除,此时,不会在等待而是自己主动清除。

    1. 释放binder_proc的todo列表(里面是binder_work),这个是等待binder_thread处理完才处理的工作列表。还有delivered_death
  • 6.释放映射地址,通过mmap映射的共享地址,此时需要释放回去。

  • 7.释放binder_proc 以上6个步骤读释放完数据,说binder_proc已经没有任何数据了,可以直接释放binder_proc数据。

释放binder_proc的binder_thread链表

作为事务传递为核心的binder驱动,那么binder_free_thread这个方法就显得尤为重要。

static int binder_free_thread(struct binder_proc *proc,
                  struct binder_thread *thread)
{
    struct binder_transaction *t;
    struct binder_transaction *send_reply = NULL;
    int active_transactions = 0;
//删除节点
    rb_erase(&thread->rb_node, &proc->threads);
//获取事务栈,找到目标
    t = thread->transaction_stack;
    if (t && t->to_thread == thread)
        send_reply = t;
    while (t) {
        active_transactions++;
        if (t->to_thread == thread) {
            t->to_proc = NULL;
            t->to_thread = NULL;
            if (t->buffer) {
                t->buffer->transaction = NULL;
                t->buffer = NULL;
            }
            t = t->to_parent;
        } else if (t->from == thread) {
            t->from = NULL;
            t = t->from_parent;
        } else
            BUG();
    }
//需要应答的,则发送BR_DEAD_REPLY上当用户空间
    if (send_reply)
        binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
//清除todo列表
    binder_release_work(&thread->todo);
//释放thread
    kfree(thread);
    binder_stats_deleted(BINDER_STAT_THREAD);
    return active_transactions;
}
  • 1.先从binder_proc的擦除threads擦除红黑树根部的binder_thread.
  • 2.从事务依赖栈寻找哪些事务想要发送当前进程。一旦发现想要发现要发送当前这个即将关闭的进程的任务,就代替这些任务并且直接发送BR_DEAD_REPLY命令。
  • 3.处理好这些任务之后,再去使用binder_release_work清空todo列表
  • 4.释放binder_thread结构体。

那么值得关注的核心逻辑有两个:

  • 1.binder_send_failed_reply 发送死亡命令
  • 2.binder_release_work释放todo 链表

binder_send_failed_reply 发送死亡命令

static void binder_send_failed_reply(struct binder_transaction *t,
                     uint32_t error_code)
{
    struct binder_thread *target_thread;
    struct binder_transaction *next;

    BUG_ON(t->flags & TF_ONE_WAY);
    while (1) {
        target_thread = t->from;
        if (target_thread) {
            if (target_thread->return_error != BR_OK &&
               target_thread->return_error2 == BR_OK) {
                target_thread->return_error2 =
                    target_thread->return_error;
                target_thread->return_error = BR_OK;
            }
            if (target_thread->return_error == BR_OK) {
                ....
                binder_pop_transaction(target_thread, t);
                target_thread->return_error = error_code;
                wake_up_interruptible(&target_thread->wait);
            } else {
....
            }
            return;
        }
        next = t->from_parent;
...
        binder_pop_transaction(target_thread, t);
        if (next == NULL) {
...
            return;
        }
        t = next;
...
    }
}

实际上,这里的逻辑和之前binder_transaction很相似。不同的是,这里会一直寻找事务栈中的binder_transaction所有的事务,换新正在阻塞的对端进程,并且弹出。在这里表现当App进程死亡之后,所有的往其他进程通信的事务,全部弹出,并且唤醒(binder_thread状态正常为BR_OK),把BR_DEAD_REPLY命令发送过去设置当对端对应的binder_thread的返回码,告诉他们此时提供服务的进程已经死亡。

由此可知,实际上能够触发读取对端( AMS) 读取数据的情景有两个,一个是一开始当AMS发送注册死亡通知时候发现此时已经死亡了,则立即唤醒自己端进行读取数据。另一个就是在当前进程(App)死亡时候发送死亡相应BR_DEAD_REPLY。

此时就开始分开两个进程同时进行工作,一个App死亡继续回收资源,一个是AMS被唤醒,读取数据。我们这里继续看看App死亡之后binder_release_work方法。

binder_release_work

实际上我们从上面的binder_free_thread代码段发现,实际上是处理发送给那些需要当前进程(此时的场景是App进程)回应的消息。换句话说,就是在处理发送出去事务。

那么binder_release_work实际上就是在处理读取的命令。

static void binder_release_work(struct list_head *list)
{
    struct binder_work *w;

    while (!list_empty(list)) {
        w = list_first_entry(list, struct binder_work, entry);
        list_del_init(&w->entry);
        switch (w->type) {
        case BINDER_WORK_TRANSACTION: {
            struct binder_transaction *t;

            t = container_of(w, struct binder_transaction, work);
            if (t->buffer->target_node &&
                !(t->flags & TF_ONE_WAY)) {
                binder_send_failed_reply(t, BR_DEAD_REPLY);
            } else {
                t->buffer->transaction = NULL;
                kfree(t);
                binder_stats_deleted(BINDER_STAT_TRANSACTION);
            }
        } break;
        case BINDER_WORK_TRANSACTION_COMPLETE: {
            kfree(w);
            binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
        } break;
        case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
        case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
            struct binder_ref_death *death;

            death = container_of(w, struct binder_ref_death, work);
            kfree(death);
            binder_stats_deleted(BINDER_STAT_DEATH);
        } break;
        default:
            break;
        }
    }
}

这里处理的工作有4种:

  • BINDER_WORK_TRANSACTION 读取todo链表,释放工作项,需要回复则模拟上面的情况,发送BR_DEAD_REPLY。

  • BINDER_WORK_TRANSACTION_COMPLETE 遇到这个和之前的处理一样直接释放当前的事务工作

  • BINDER_WORK_DEAD_BINDER_AND_CLEAR/BINDER_WORK_CLEAR_DEATH_NOTIFICATION:遇到需要清理的死亡恢复则从工作项中找到death对象直接清理。

binder_delete_ref

该方法将会删除binder引用对象

static void binder_delete_ref(struct binder_ref *ref)
{
...

    rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc);
    rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node);
    if (ref->strong)
        binder_dec_node(ref->node, 1, 1);
    hlist_del(&ref->node_entry);
    binder_dec_node(ref->node, 0, 1);
    if (ref->death) {
...
        list_del(&ref->death->work.entry);
        kfree(ref->death);
        binder_stats_deleted(BINDER_STAT_DEATH);
    }
    kfree(ref);
    binder_stats_deleted(BINDER_STAT_REF);
}

从上刻面可以得知,此时需要从两个管理binder_ref的共黑树删除当前的引用,以及节点指针。最后,如果发现引用中还有死亡回调,则删除death->work.entry。

对端(AMS)被唤醒,读取数据

此时我们将会从对端AMS开始读取数据看看binder_thread_read是怎么回事

case BINDER_WORK_DEAD_BINDER:
        case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
        case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
            struct binder_ref_death *death;
            uint32_t cmd;

            death = container_of(w, struct binder_ref_death, work);
            if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)
                cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
            else
                cmd = BR_DEAD_BINDER;
            if (put_user(cmd, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);
            if (put_user(death->cookie,
                     (binder_uintptr_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(binder_uintptr_t);
            binder_stat_br(proc, thread, cmd);
            if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {
                list_del(&w->entry);
                kfree(death);
                binder_stats_deleted(BINDER_STAT_DEATH);
            } else
                list_move(&w->entry, &proc->delivered_death);
            if (cmd == BR_DEAD_BINDER)
                goto done; /* DEAD_BINDER notifications can cause transactions */
        } break;

此时,对端AMS将会开始读取todo中的命令。这里依据App进程的binder_transaction方法中。如果当目标进程还没有死亡,此时在todo中并不会读取到BINDER_WORK_DEAD_BINDER命令。但是当App死亡之后,就存在了BINDER_WORK_DEAD_BINDER命令。

在这个分支处理了三种命令,一种是注册死亡通知的命令BINDER_WORK_DEAD_BINDER,另外两种是清空死亡通知。我们现在只关心第一种。

我们能看到的是从todo找到死亡引用对象(death_ref)把命令转化为BR_DEAD_BINDER并且把数据拷贝到用户空间。

接下来,看看在IPCThreadState中的looper,怎么获取数据。

IPCThreadState::executeCommand

文件:/frameworks/native/libs/binder/IPCThreadState.cpp

    case BR_DEAD_BINDER:
        {
            BpBinder *proxy = (BpBinder*)mIn.readPointer();
            proxy->sendObituary();
            mOut.writeInt32(BC_DEAD_BINDER_DONE);
            mOut.writePointer((uintptr_t)proxy);
        } break;

这里分为两步:

  • 1.获取到BpBinder之后调用了sendObituary方法。
  • 2.继续往binder驱动写入BC_DEAD_BINDER_DONE。当该方法发送之后回删除之前注册的时候初始化的death->work.entry

BpBinder::sendObituary

void BpBinder::sendObituary()
{
    mAlive = 0;
    if (mObitsSent) return;

    mLock.lock();
    Vector<Obituary>* obits = mObituaries;
    if(obits != NULL) {
        IPCThreadState* self = IPCThreadState::self();
        self->clearDeathNotification(mHandle, this);
        self->flushCommands();
        mObituaries = NULL;
    }
    mObitsSent = 1;
    mLock.unlock();

    if (obits != NULL) {
        const size_t N = obits->size();
        for (size_t i=0; i<N; i++) {
            reportOneDeath(obits->itemAt(i));
        }

        delete obits;
    }
}

在这里面还是分为两步:

  • 1.调用clearDeathNotification,往BC_CLEAR_DEATH_NOTIFICATION发送命令。
  • 2.查看vector向量中绑定了多少的死亡代理,并且逐一调用reportOneDeath方法,报告上层。

BC_CLEAR_DEATH_NOTIFICATION命令

该命令和上面的注册死亡代理相对应

case BC_REQUEST_DEATH_NOTIFICATION:
        case BC_CLEAR_DEATH_NOTIFICATION: {
            uint32_t target;
            binder_uintptr_t cookie;
            struct binder_ref *ref;
            struct binder_ref_death *death;

            if (get_user(target, (uint32_t __user *)ptr))//BC_CLEAR_DEATH_NOTIFICATION
                return -EFAULT;
            ptr += sizeof(uint32_t);
            if (get_user(cookie, (binder_uintptr_t __user *)ptr))//BpBinder
                return -EFAULT;
            ptr += sizeof(binder_uintptr_t);
            ref = binder_get_ref(proc, target);
            if (ref == NULL) {
                break;
            }

            if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
....
            } else {
                if (ref->death == NULL) {
...
                    break;
                }
                death = ref->death;
                if (death->cookie != cookie) {
...
                    break;
                }
                ref->death = NULL;
                if (list_empty(&death->work.entry)) {
                    death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;
                    if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
                        list_add_tail(&death->work.entry, &thread->todo);
                    } else {
                        list_add_tail(&death->work.entry, &proc->todo);
                        wake_up_interruptible(&proc->wait);
                    }
                } else {
...
                    death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR;
                }
            }
        } break;

在这里处理和上面处理BC_REQUEST_DEATH_NOTIFICATION几乎一致,不同的是,当判断到todo没有任何工作项,则设置一个新的到队尾,唤醒当前进程继续读取数据。否则什么都不做,只是找到对应的binder引用,转化death的类型为BINDER_WORK_DEAD_BINDER_AND_CLEAR。

当前进程(AMS)继续读取数据

if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {
                list_del(&w->entry);
                kfree(death);
                binder_stats_deleted(BINDER_STAT_DEATH);
            }

此时的工作很简单,就是把当前进程中binder_ref的对应的death对象释放了。

因此BC_CLEAR_DEATH_NOTIFICATION命令到头来只是释放了death的内存,让其不继续回调。

reportOneDeath 反射回java层

void BpBinder::reportOneDeath(const Obituary& obit)
{
    sp<DeathRecipient> recipient = obit.recipient.promote();
    if (recipient == NULL) return;

    recipient->binderDied(this);
}

此时获取了Obituary中之前设置的DeathRecipient结构体,在这里是指JavaDeathRecipient类。我们看看binderDied的方法。
文件:/frameworks/base/core/jni/android_util_Binder.cpp

   void binderDied(const wp<IBinder>& who)
    {
...
        if (mObject != NULL) {
            JNIEnv* env = javavm_to_jnienv(mVM);

            env->CallStaticVoidMethod(gBinderProxyOffsets.mClass,
                    gBinderProxyOffsets.mSendDeathNotice, mObject);
...
        }
    }

这里就能看到核心调用方法,反射调用java层BinderProxy的sendDeathNotice方法。

    private static final void sendDeathNotice(DeathRecipient recipient) {
        if (false) Log.v("JavaBinder", "sendDeathNotice to " + recipient);
        try {
            recipient.binderDied();
        }
        catch (RuntimeException exc) {
            Log.w("BinderNative", "Uncaught exception from death notification",
                    exc);
        }
    }

这个recipient不就是我们开头一开始就注册好的AppDeathRecipient的类吗?刚好就回调到binderDied中。

总结

至此,linkToDeath整个流程就完成。实际上这个方法的作用就能下定论,当linkToDeath被注册起来之后,当前的进程的binder对象就会持续对远程端的binder观测其是否存活。

当对端出现了异常导致退出或者进程结束,放到当前的场景就是App进程结束或者闪退,则回通知到对对方观测的binder对象一个死亡回调,AMS就会清除存放在内部相关的Activity的信息,并且销毁回调不再继续监听对方的死亡。

所以,这个设计的核心思想如下:
当远程端没有死亡的时候或者正在死亡,根本不会做任何加入到本身进程的todo链表中,让其读取到命令到回调。一旦远程端死亡了,则会立即把这个对象添加到todo链表中,让对方端唤醒执行回调。

下面是linkToDeath时序图:


binder 注册死亡代理.png

下面是当App进程时候的死亡回调时序图


binder死亡唤起死亡注册的回调.png

注意:红色线代表了跨进程

结束语

到这里,我就把Binder大致上大大小小的所有的细节大致上都过了一边。整整花了6篇文章,也没办法把Binder的细节处处到位,仅仅只是过了大体的思想以及主要思路。还有binder的poll操作,binder的数据结构rb_tree,hlist等等。

不过在这个过程中,翻阅不少资料,特别是相关于Linux的内核知识以确定当初的学习是否正确。比起两年前当初看Binder就头疼,现在能抓住Binder主要思想来说已经有了不少进步。

Binder只能说是Android的核心之一,但是还有另一个核心当然是四大组件以及底层的绘制原理。里面涉及了opengl,还有linux的gui体系等等。接下来的计划,将会开始写Android 9.0中的Activity的启动流程,写完启动流程之后,因为会涉及到WindowManager,那么必须聊聊opencv,opengl,ffmpeg这几个经典的音视频第三库。

虽然opengl那一块几乎不怎么会,但为了弄明白Android的绘制原理这个坎必须经过。因此之后opengl的文章将会作为学习笔记放出。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343