binder驱动-订阅binder实体死亡通知

 

       前面一篇文章介绍了binder通讯的实现,这篇讨论下这个在binder通讯中比较重要的通知机制:binder实体死亡通知。虽然说binder实体只要存在强引用就不会被销毁,但是这毕竟是跨进程的引用,谁也无法保证binder实体所在server关闭或者binder驱动一次退出而消失,这个时候就尽量要求在server退出时给出通知。而这个死亡通知也不是见谁就发的,只有通过命令BC_REQUEST_NOTIFICATION告诉过binder驱动本进程需要知道binder实体已经销毁的进程才会收到这个通知。

      

一、 相关命令和数据结构

@ kernel/drivers/staging/android/binder.h

struct binder_write_read结构体的write_buffer中记录的是BC命令和数据,BC命令定义如下:

enum BinderDriverCommandProtocol {

       BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),

       BC_REPLY = _IOW('c', 1, struct binder_transaction_data),

       …

       BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14, struct binder_ptr_cookie),

       // 获得Binder引用的进程通过该命令要求驱动在Binder实体销毁时得到通知。

       BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15, struct binder_ptr_cookie),

       // 取消订阅binder实体死亡通知。

       BC_DEAD_BINDER_DONE = _IOW('c', 16, void *),

       // 收到实体死亡通知书的进程在删除引用后用本命令告知驱动。

}

struct binder_ptr_cookie {

       void *ptr;              // 指向binder实体对应的引用号的指针

       void *cookie;  // 使用binder引用号的进程写进binder驱动中去的额外数据,已在取消订阅时进行校验,已明确身份。

};

 

struct binder_write_read结构体的read_buffer中记录的是BR命令和数据,BR命令定义如下:

enum BinderDriverReturnProtocol {

       …

       BR_DEAD_BINDER = _IOR('r', 15, void *),

       // 当前task接收到的binder实体死亡通知

       BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, void *),

       // 当前task取消订阅binder死亡通知成功之后得到的回复命令

       …

}

 

只要某进程对某binder引用订阅了其实体的死亡通知,那么binder驱动将会为该binder引用建立一个通知结构体:binder_ref_death,将其保存在当前进程的对应binder引用结构体的death域中。

struct binder_ref_death {

       struct binder_work work;

       void __user *cookie;

};

 

struct binder_ref {

       …

       struct binder_ref_death *death;

};

 

二、 代码分析

订阅、取消通知或者返回确认命令给binder驱动,都将是通过ioctl的BINDER_WRITE_READ带上binder_write_read结构体进入内核空间的。只不过需要在write_buffer指向的空间中加上BC_REQUEST_DEATH_NOTIFICATION、BC_CLEAR_DEATH_NOTIFICATION、BC_DEAD_BINDER_DONE,当然还需要参数数据binder_ptr_cookie,这个结构体中包含用户空间中binder引用号变量的指针和私有cookie数据的指针。

       这部分的代码位于函数binder_thread_write()和binder_thread_read()之中,下面只列出代码片段,完整的代码请参考源码。

 

2.1 请求和取消订阅死亡通知

case BC_REQUEST_DEATH_NOTIFICATION:         // 订阅binder死亡通知

case BC_CLEAR_DEATH_NOTIFICATION: {          // 取消订阅binder死亡通知

       uint32_t target;

       void __user *cookie;

       struct binder_ref *ref;

       struct binder_ref_death *death;

      

       /* 获取用户空间传下来的参数 */

       if (get_user(target, (uint32_t __user *)ptr))

              return -EFAULT;

       ptr += sizeof(uint32_t);

       if (get_user(cookie, (void __user * __user *)ptr))

              return -EFAULT;

       ptr += sizeof(void *);

      

       ref = binder_get_ref(proc, target);

       // 根据binder引用号得到位于当前进程中的binder_ref结构体

       if (ref == NULL) {

       …// 错误处理,如果为NULL,表明当前binder引用号无效

       }

       …

       if (cmd == BC_REQUEST_DEATH_NOTIFICATION) { // 请求订阅
              if (ref->death) {            // 当前进程已经订阅过该binder实体的死亡通知了

                     …

                     break;

              }

              death = kzalloc(sizeof(*death), GFP_KERNEL);

              …

              binder_stats_created(BINDER_STAT_DEATH);   // 全局统计计数

              INIT_LIST_HEAD(&death->work.entry);// binder_ref_death中的binder_work初始化

              death->cookie = cookie;       

              ref->death = death;

              /* 实际上到这里,订阅binder死亡通知的工作基本完成,不过为了接下来还要检查对应的binder实体是否已经不存在了。 */

              if (ref->node->proc == NULL) {

                     ref->death->work.type = BINDER_WORK_DEAD_BINDER;

                     // 如果实体不存在了就将binder_work的类设置成该值,表示binder实体死亡

                    if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {

                            list_add_tail(&ref->death->work.entry, &thread->todo);

                     } else {/* 如果binder_thread表示的task以就绪,那么将该work加入当前task的私有todo队列中,否则加入进程的全局todo中,然后唤醒进程的空闲线程等待队列。*/

                            list_add_tail(&ref->death->work.entry, &proc->todo);

                            wake_up_interruptible(&proc->wait);

                     }

              }

} else { // 取消订阅

              if (ref->death == NULL) {

                     …    // 错误处理,如果当前进程没有为该binder实体订阅死亡通知

                     break;

              }

              death = ref->death;

              if (death->cookie != cookie) { // cookie数据验证,之前订阅是保存在驱动中的和当前ioctl传下来的cookie比较。

                     …

                     break;

              }

              ref->death = NULL;             // 该域置为NULL

              if (list_empty(&death->work.entry)){ // 无当前binder实体的死亡通知需要处理。

                     death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;

                     // 那么就直接将binder_work的类型定义成该值,表示只取消订阅通知。                     

                     if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {

                            list_add_tail(&death->work.entry, &thread->todo);

                            /* 这里为什么不用wake_up一下thread->wait呢?这个wait中经常是没有task在等待的,即使有,也是在同步通信的时候将自己挂进这个wait中等待,没有其他情况了,所以这里首先判断这个task是不是已经在进入循环开始工作了,如果是的话,可以将当前work加入到task的私有todo中,这样在后面的通讯过程中,迟早会处理这个work任务的。 */

                     } else {

                            list_add_tail(&death->work.entry, &proc->todo);

                            wake_up_interruptible(&proc->wait);/* 个人觉得,这里也可以不用 */

                     }

              } else {

                     BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER);

                     death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR;

                     /* 如果在取消订阅之前已经有收到当前对应binder实体的死亡通知了,也就是&death->work.entry已经存在于todo队列中。这个时候将binder_work类型修改为BINDER_WORK_DEAD_BINDER_AND_CLEAR,表示需要先处理死亡通知,再处理取消取消订阅的请求。*/

              }

} // else 取消订阅

} break;

 

2.2 驱动发送binder死亡通知

       虽然2.1中已经有发送死亡通知的情况出现,但是那些情况都是在刚刚请求了或者取消死亡通知时做的临时检查,这个检查是必要的,不过binder驱动中真正发送死亡通知的地方在哪里呢?

       在函数binder_deferred_release()中,会做如下工作:

while ((n = rb_first(&proc->nodes))) {

       struct binder_node *node = rb_entry(n, struct binder_node, rb_node);

 

       nodes++;

       rb_erase(&node->rb_node, &proc->nodes);

       list_del_init(&node->work.entry);

       if (hlist_empty(&node->refs)) {// 如果当前node不存在refs队列,直接释放node的空间

              kfree(node);

              binder_stats_deleted(BINDER_STAT_NODE);

       } else {   // 存在多个binder_ref结构体和当前的binder_node有关。

              struct binder_ref *ref;

              int death = 0;

             

              node->proc = NULL; // binder实体死亡标志,前面请求通知时就是通过这个判断的

              node->local_strong_refs = 0;

              node->local_weak_refs = 0;

              …

              hlist_for_each_entry(ref, pos, &node->refs, node_entry) {

                     incoming_refs++;

                     if (ref->death) {

                            death++;

                            if (list_empty(&ref->death->work.entry)) {

                                   ref->death->work.type = BINDER_WORK_DEAD_BINDER;

                                   list_add_tail(&ref->death->work.entry, &ref->proc->todo);

                                   wake_up_interruptible(&ref->proc->wait);

                                   /* 这里是将binder_work加入到进程的全家todo队列中的 */

                            } else

                                   BUG();

                     }

              } // hlist_for_each_entry(ref, pos, &node->refs, node_entry)

       } // else

       …

} // while

 

2.3 binder_thread_read函数中对这些binder_work的处理

       其实上面2.1中,请求和取消死亡通知是比较简单的,binder驱动就可以单方向完成。不过在请求和取消时临时检查了下binder实体是否死亡和是否有binder死亡通知存在,如果有这两种情况出现,那么就需要请求或取消的task在binder_thread_read的时候对特定类型的binder_work进行处理。

switch (w->type) {

       …

       case BINDER_WORK_DEAD_BINDER:    // 表示binder实体已经死亡

       case BINDER_WORK_DEAD_BINDER_AND_CLEAR:// 取消订阅之前有收到某binder实体死亡通知

       case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {// 取消binder实体死亡通知

              struct binder_ref_death *death;

              uint32_t cmd;

             

              death = container_of(w, struct binder_ref_death, work); // 得到保护这个binder_work的binder_ref_death结构体指针。

              if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)

                     cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE; //  binder驱动返回的取消binder死亡通知订阅的确认命令

              else

                     cmd = BR_DEAD_BINDER; //  binder驱动返回的binder实体死亡的命令

             

              if (put_user(cmd, (uint32_t __user *)ptr))

                     return -EFAULT;

              ptr += sizeof(uint32_t);

              if (put_user(death->cookie, (void * __user *)ptr))

                     return -EFAULT;

              ptr += sizeof(void *);

              …

             

              if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {

                     list_del(&w->entry);

                     kfree(death);   // 取消订阅,释放binder_ref_death内存空间

                     binder_stats_deleted(BINDER_STAT_DEATH);

              } else

                     /* 将binder_work移动到delivered_death队列, 等待task上层空间处理完之后,发送BC_ DEAD_BINDER_DONE命令给驱动之后,驱动再从这里面取出binder_work作处理。*/

                     list_move(&w->entry, &proc->delivered_death);

             

              if (cmd == BR_DEAD_BINDER)

                     goto done;     /* DEAD_BINDER notifications can cause transactions */

       } break;

} // switch (w->type)

 

2.4 task上层收到通知后给驱动发送确认命令

       // 收到实体死亡通知书的进程在删除引用后用本命令告知驱动。

case BC_DEAD_BINDER_DONE: {

       struct binder_work *w;

       void __user *cookie;

       struct binder_ref_death *death = NULL;

       if (get_user(cookie, (void __user * __user *)ptr))

              return -EFAULT;

       ptr += sizeof(void *);

 

       list_for_each_entry(w, &proc->delivered_death, entry) {

              struct binder_ref_death *tmp_death = container_of(w, struct binder_ref_death, work);

              if (tmp_death->cookie == cookie) {

                     death = tmp_death;

                     break;

              }

       } // 找到分离出去的binder_work,以cookie为依据。

       …

       if (death == NULL) {    // 没有找到对应的binder_ref_death结构体

              binder_user_error("binder: %d:%d BC_DEAD"

                            "_BINDER_DONE %p not found\n",

                            proc->pid, thread->pid, cookie);

              break;

       }

      

       list_del_init(&death->work.entry);              // 从列表上分离出来

       if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) {

              /* 如果在取消订阅的时候有未处理的死亡通知,原则上是按照先把死亡通知处理完之后,再来处理取消订阅通知。这里接下来就是添加一个取消订阅通知的    binder_work。 */

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

              }

       }

} break;

 

完!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值