当前位置: 代码迷 >> 综合 >> android Binder系列--死亡通知机制
  详细解决方案

android Binder系列--死亡通知机制

热度:69   发布时间:2024-02-02 04:43:05.0

   如果在binder通信已经建立的情况下,出现binder服务端的进程因为内部错误或其它情况意外挂掉,这个时候就需要通知到binder代理端,好让代理端作出相应的操作,以保证代理端进程不会因为binder服务端进程的意外挂掉而出错。这个就是binder提供的死亡通知机制。

  在这种死亡机制中,首先是Binder代理对象(BpBinder)将一个死亡接收通知注册到Binder驱动中,然后当Binder驱动检测到它所引用的Binder本地对象(BBinder)死亡时,Binder本地对象在驱动中是用binder节点(binder_node)形式表示的,Binder驱动就会向它发送一个死亡通知。

      当然,如果一个Binder代理对象(BpBinder)不需要接收它所引用的Binder本地对象(BBinder)的死亡通知时,它也可以注销之前所注册的死亡接受通知。

  我们从死亡通知的注册,注销,触发和处理四个方面分别阐述其过程,以了解其原理。最后分析下java层如何通过JNI和native进行通信以及使用例子。

相关代码路徑:

frameworks/base/core/java/android/os/Binder.java
frameworks/base/core/jni/android_util_Binder.cpp
frameworks/native/libs/binder/BpBinder.cpp

一 死亡通知的注册

Binder代理对象(BpBinder)通过linkToDeath函数实现对死亡通知的注册,代码如下:

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;}ALOGV("Requesting death notification: %p handle %d\n", this, mHandle);getWeakRefs()->incWeak(this);IPCThreadState* self = IPCThreadState::self();self->requestDeathNotification(mHandle, this);// 向mOut写入注册死亡通知的命令self->flushCommands();}ssize_t res = mObituaries->add(ob);return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res;}}return DEAD_OBJECT;
}

以上死亡通知的注册函数有以下几个关键变量需要注意:

mObituaries是一个向量表,里边是Obituary的集合,而Obituary是封装了死亡接收通知对象DeathRecipient,以及Binder代理对象地址(cookie)的一个对象,我们可以认为每一个Obituary就代表注册了一个死亡接收通知对象,从代码中可以看出一个Binder代理对象可以注册多个死亡接收通知Obituary,并把这些注册好的Obituary放到向量表mObituaries中.

mObitsSent这个变量代表Binder驱动是否已经发送了死亡通知,如果已经发送了则为1,否则为0,如果已经发送了,则就不需要注册了,直接返回DEAD_OBJECT

从以上代码中还可以看到注册死亡通知的函数requestDeathNotification只会在第一次创建mObituaries的时候会被执行.其它情况下只需要把死亡接收通知Obituary直接添加到mObituaries中既可,不需要多次注册.

IPCThreadState.cpp

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;
}

从以上代码中可以看到requestDeathNotification就是把BC_REQUEST_DEATH_NOTIFICATION,以及代理端的句柄handle,还有Binder代理对象的引用proxy三个参数写入了mOut中,其中BC_REQUEST_DEATH_NOTIFICATION就是向Binder驱动注册死亡接收通知的命令.

接着看binder驱动如何处理BC_REQUEST_DEATH_NOTIFICATION命令,代码如下:

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_REQUEST_DEATH_NOTIFICATION:case BC_CLEAR_DEATH_NOTIFICATION: {uint32_t target; //BpBinder对应的句柄值,用来找到对应的binder_refbinder_uintptr_t cookie; //代理对象BpBinder的地址struct binder_ref *ref;struct binder_ref_death *death = NULL; //binder_ref对应的死亡通知if (get_user(target, (uint32_t __user *)ptr)) //从用户空间获取代理对象的句柄值return -EFAULT;ptr += sizeof(uint32_t);if (get_user(cookie, (binder_uintptr_t __user *)ptr))//获取代理对象的地址return -EFAULT;ptr += sizeof(binder_uintptr_t);if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {/** Allocate memory for death notification* before taking lock*/death = kzalloc(sizeof(*death), GFP_KERNEL);if (death == NULL) {WARN_ON(thread->return_error.cmd !=BR_OK);thread->return_error.cmd = BR_ERROR;binder_enqueue_thread_work(thread,&thread->return_error.work);binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,"%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n",proc->pid, thread->pid);break;}}binder_proc_lock(proc);ref = binder_get_ref_olocked(proc, target, false);//根据句柄获得对应的binder_refif (ref == NULL) {binder_user_error("%d:%d %s invalid ref %d\n",proc->pid, thread->pid,cmd == BC_REQUEST_DEATH_NOTIFICATION ?"BC_REQUEST_DEATH_NOTIFICATION" :"BC_CLEAR_DEATH_NOTIFICATION",target);binder_proc_unlock(proc);kfree(death);break;}.......................binder_node_lock(ref->node);if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {if (ref->death) {binder_user_error("%d:%d BC_REQUEST_DEATH_NOTIFICATION death notification already set\n",proc->pid, thread->pid);binder_node_unlock(ref->node);binder_proc_unlock(proc);kfree(death);break;}binder_stats_created(BINDER_STAT_DEATH);INIT_LIST_HEAD(&death->work.entry);death->cookie = cookie;ref->death = death; //初始化binder_ref_death并赋值给ref的death,完成死亡通知的注册if (ref->node->proc == NULL) {// 代理对象对应的服务端进程已经死亡(异常情况)ref->death->work.type = BINDER_WORK_DEAD_BINDER;binder_inner_proc_lock(proc);binder_enqueue_work_ilocked(&ref->death->work, &proc->todo);//把节点死亡的work添加到代理端进程的等待队列todo中binder_wakeup_proc_ilocked(proc);//唤醒代理端进程binder_inner_proc_unlock(proc);}} else {......................}binder_node_unlock(ref->node);binder_proc_unlock(proc);} break;

从以上代码可以看到向Binder驱动注册死亡接收通知非常简单,其实就是新建一个binder_ref_death对象,把代表Binder代理对象的cookie赋值给binder_ref_death的cookie,并初始化wrok,然后把binder_ref_death对象赋值给代理对象对应的binder_ref的death就可以了.以后binder驱动会根据binder_ref的death是否存在来决定是否向其发送死亡通知.从代码中可以看到对binder_ref的death赋值完毕后,会有一个ref->node->proc是否为空的判断,也就是在刚注册完死亡通知,立马判断服务端进程是否已经挂掉,如果已经挂掉了,那么把这个death的work赋值为BINDER_WORK_DEAD_BINDER并添加到代理端进程的等待队列todo中,并唤醒代理端进程,处理BINDER_WORK_DEAD_BINDER任务

struct binder_ref_death {/*** @work: worklist element for death notifications*        (protected by inner_lock of the proc that*        this ref belongs to)*/struct binder_work work;binder_uintptr_t cookie;
};

死亡接收通知的注册已经讲完了,说白了就是对Binder代理对象驱动层对应的binder_ref的death赋值,只要有了这个death值,驱动就知道这个binder引用注册了死亡接收通知.这样一旦服务端进程意外挂掉的话,binder驱动就会遍历服务端进程中binder_node的引用树,并检查其中每个binder_ref的death值,如果不为空,则就向其发送死亡通知.

二 死亡通知的注销

Binder代理对象(BpBinder)通过unlinkToDeath函数实现对死亡通知的注销的,代码如下:

BpBinder.cpp

status_t BpBinder::unlinkToDeath(const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,wp<DeathRecipient>* outRecipient)
{AutoMutex _l(mLock);if (mObitsSent) {//已经发送过死亡通知了直接返回return DEAD_OBJECT;}const size_t N = mObituaries ? mObituaries->size() : 0;for (size_t i=0; i<N; i++) {const Obituary& obit = mObituaries->itemAt(i);if ((obit.recipient == recipient|| (recipient == NULL && obit.cookie == cookie))&& obit.flags == flags) {if (outRecipient != NULL) {*outRecipient = mObituaries->itemAt(i).recipient;}mObituaries->removeAt(i);//移除注册过的死亡接收通知if (mObituaries->size() == 0) {ALOGV("Clearing death notification: %p handle %d\n", this, mHandle);IPCThreadState* self = IPCThreadState::self();self->clearDeathNotification(mHandle, this);//全部移除完毕了,才会向驱动发送清除死亡消息的命令self->flushCommands();delete mObituaries;mObituaries = NULL;}return NO_ERROR;}}return NAME_NOT_FOUND;
}

从以上代码可以看到Binder代理通过unlinkToDeath函数来移除注册过的死亡接收通知,当注册过的死亡通知全部被移除后,才会向Binder驱动发送注销死亡接收通知的命令,通注册时候一样,注销也是只向驱动发送一次注销命令

IPCThreadState.cpp

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

把BC_CLEAR_DEATH_NOTIFICATION命令,handle,proxy添加到mOut中,然后发送给binder驱动,下面看驱动如何处理

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_REQUEST_DEATH_NOTIFICATION:case BC_CLEAR_DEATH_NOTIFICATION: {uint32_t target;binder_uintptr_t cookie;struct binder_ref *ref;struct binder_ref_death *death = NULL;if (get_user(target, (uint32_t __user *)ptr))return -EFAULT;ptr += sizeof(uint32_t);if (get_user(cookie, (binder_uintptr_t __user *)ptr))return -EFAULT;ptr += sizeof(binder_uintptr_t);.................binder_proc_lock(proc);ref = binder_get_ref_olocked(proc, target, false);//获取对应的binder_refif (ref == NULL) {binder_user_error("%d:%d %s invalid ref %d\n",proc->pid, thread->pid,cmd == BC_REQUEST_DEATH_NOTIFICATION ?"BC_REQUEST_DEATH_NOTIFICATION" :"BC_CLEAR_DEATH_NOTIFICATION",target);binder_proc_unlock(proc);kfree(death);break;}.............................binder_node_lock(ref->node);if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {.......................} else {if (ref->death == NULL) {binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification not active\n",proc->pid, thread->pid);binder_node_unlock(ref->node);binder_proc_unlock(proc);break;}death = ref->death;if (death->cookie != cookie) {binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch %016llx != %016llx\n",proc->pid, thread->pid,(u64)death->cookie,(u64)cookie);binder_node_unlock(ref->node);binder_proc_unlock(proc);break;}ref->death = NULL;//把binder_ref的death赋值为null,实现对死亡接收通知的注销操作binder_inner_proc_lock(proc);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))binder_enqueue_thread_work_ilocked(thread,&death->work);//当前线程为binder线程,则直接添加到当前线程的todo队列. else {binder_enqueue_work_ilocked(&death->work,&proc->todo);binder_wakeup_proc_ilocked(proc);//否则添加到进程的todo队列}} else {BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER);death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR;}binder_inner_proc_unlock(proc);}binder_node_unlock(ref->node);binder_proc_unlock(proc);} break;

以上对死亡接收通知的注销操作主要做了二件事情,一件是把binder代理对象对应的binder引用的binder_ref的death对象赋值为null.这样驱动就不会再向其发送死亡通知了.另外一件事情是把注销死亡通知消息的work添加到 根据death的wrok的entry是否为空来判断

  相关解决方案