Binder结点管理

来源:互联网 发布:时间压缩 知乎 编辑:程序博客网 时间:2024/06/07 02:18

Binder驱动中,Binder结点随着进程IPC通信开始而生,随着进程IPC通信结束而亡,换句话说,Binder结点是维系IPC通信的基础,而且一个Binder结点也只能在完全无IPC通信的情况下被删除。本文将重点研究Binder结点从生到死的过程。

Binder结点作为IPC通信过程中的实体,是IPC通信的媒介。在描述一个进程中Binder信息的数据结构中,与结点相关的成员变量有:

struct binder_proc {       struct hlist_node proc_node;// list node for global binder_procs hlist       ...       struct rb_root nodes;       struct rb_root refs_by_desc;       struct rb_root refs_by_node;       ...};

其中,nodes记录了当前进程创建的Binder结点。而refs_by_descrefs_by_node都记录的是Binder结点引用的链表。nodes链表的大小反映了有多少进程在向当前进程请求服务,而结点引用链表的大小则反映了当前进程在向哪些进程请求服务。

创建Binder结点的函数是binder_new_node。在分析此函数之前,我们先分析一下Binder驱动中结点的数据结构定义:

struct binder_node {       int debug_id;       struct binder_work work;       union {              struct rb_node rb_node;              struct hlist_node dead_node;       };       struct binder_proc *proc;       struct hlist_head refs;       int internal_strong_refs;       int local_weak_refs;       int local_strong_refs;       void __user *ptr;       void __user *cookie;       unsigned has_strong_ref:1;       unsigned pending_strong_ref:1;       unsigned has_weak_ref:1;       unsigned pending_weak_ref:1;       unsigned has_async_transaction:1;       unsigned accept_fds:1;       unsigned min_priority:8;       struct list_head async_todo;};

下面分别解释上述各个成员变量的意义:

1. debug_id: 结点的一个数字序号标记,根据全局变量binder_last_id来维护并分配,binder_last_idBinder驱动全局变量,且单调递增的。

2. work:一般类型为BINDER_WORK_NODEbinder_work,添加到线程上的todo链表中去处理,主要处理结点的引用计数。

3. 联合体:rb_node代表此结点在进程全局红黑树binder_procs上的一个结点。而dead_node代表红黑树binder_dead_nodes中的一个结点。根据当前结点的状态,将决定将结点加入到哪个全局链表中。

4. proc: 与结点相关联的binder_proc

5. refs: binder_ref链表,记录当前其他进程对该Binder结点的引用。

在binder_get_ref_for_node方法中,在其他進程創建的binder_ref實例最後都會插入到該鏈表中。

static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,  struct binder_node *node){...new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);...if (node) {hlist_add_head(&new_ref->node_entry, &node->refs);    ...}     ...return new_ref;}

6. internal_strong_refs:内部强引用计数

通過binder_inc_ref函數調用時,一般會增加該引用計數。Internal這個詞是相對binder_ref來說的,一般binder_ref都會對應一個binder_nodeinternal_strong_refs的數目實際上代表了一個binder_node有多少個binder_ref與之關聯。

binder_transaction過程中,如果目標進程有對本地進程binder結點的一個引用,將會向當前線程提交一個結點強引用處理請求,增加一個強引用指向使本地結點的。代碼如下:

static void binder_transaction(struct binder_proc *proc,

       struct binder_thread *thread,

       struct binder_transaction_data *tr, int reply)

{

...

fp = (struct flat_binder_object *)(t->buffer->data + *offp);

switch (fp->type) {

case BINDER_TYPE_BINDER:

case BINDER_TYPE_WEAK_BINDER: {

struct binder_ref *ref;

struct binder_node *node = binder_get_node(proc, fp->binder);

if (node == NULL) {

node = binder_new_node(proc, fp->binder, fp->cookie);

...

ref = binder_get_ref_for_node(target_proc, node);

if (ref == NULL) {

return_error = BR_FAILED_REPLY;

goto err_binder_get_ref_for_node_failed;

}

if (fp->type == BINDER_TYPE_BINDER)

fp->type = BINDER_TYPE_HANDLE;

else

fp->type = BINDER_TYPE_WEAK_HANDLE;

fp->handle = ref->desc;

binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,

       &thread->todo);

 

...

} break;

...


而在binder_inc_ref,會調用binder_inc_node(ref->node, 1, 1, &thread->todo), 繼續看binder_inc_node的代碼,可知,最後會將具體的結點強引用計數處理作爲一個binder_work提交給當前的線程池中的一個線程去處理了。

 

static int binder_inc_node(struct binder_node *node, int strong, int internal,

   struct list_head *target_list)

{

if (strong) {

if (internal) {

...

node->internal_strong_refs++;

} else

node->local_strong_refs++;

if (!node->has_strong_ref && target_list) {

list_del_init(&node->work.entry);

list_add_tail(&node->work.entry, target_list);

}

    ...

return 0;

}

 

即list_add_tail(&node->work.entry, target_list);會將當前結點引用的請求提交給線程池中的線程處理。

7. local_weak_refs:本地弱引用计数。

8. local_strong_refs:本地强引用计数。

一般是通過直接調用binder_inc_nodebinder_dec_node上述兩個引用計數的,這個值直接反映了binder_inc_node/binder_dec_node的調用是否匹配。

9. ptr:通常指RefBase::weakref_type實例的内存地址或遠程Binder的句柄,当其是0号结点时,该指针为空。

10. cookies: 通常指本地Binder对象的地址,在驅動中將作爲結點的索引。

 

 

Local Binder

Remote Binder

Strong Binder

BINDER_TYPE_BINDER

IBinder *local = binder->localBinder();

ptr = local->getWeakRefs()

cookie = local

BINDER_TYPE_HANDLE

 BpBinder 

*proxy = binder->remoteBinder();

ptr = handle

cookie = NULL

Weak Binder

BINDER_TYPE_WEAK_BINDER

sp<IBinder> real = binder.promote();

ptr = binder.get_refs();

cookie = binder.unsafe_get();

BINDER_TYPE_WEAK_HANDLE

 BpBinder 

*proxy = binder->remoteBinder();

ptr = handle

cookie = NULL

 

11. has_strong_ref:是否拥有强引用。

置爲1,表明其他進程有強引用指向當前結點。

11. pending_strong_ref:对结点的强引用是否处理完成(BC_ACQUIRE_DONE),會涉及用戶空間引用計數的處理。

static int binder_thread_read(struct binder_proc *proc,      struct binder_thread *thread,      void  __user *buffer, int size,      signed long *consumed, int non_block){  ...  while(1) {  ...  case BINDER_WORK_NODE: {  struct binder_node *node = container_of(w, struct binder_node, work);  uint32_t cmd = BR_NOOP;  const char *cmd_name;  int strong = node->internal_strong_refs || node->local_strong_refs;  int weak = !hlist_empty(&node->refs) || node->local_weak_refs || strong;  ...  } else if (strong && !node->has_strong_ref) {  cmd = BR_ACQUIRE;  cmd_name = "BR_ACQUIRE";  node->has_strong_ref = 1;  node->pending_strong_ref = 1;  node->local_strong_refs++;  }          ...} break;

此時,has_pending_strong_ref置爲1,等待用戶空間部分的引用計數處理。會向用戶空間發送BR_ACQUIRE命令。

IPCThreadState.cpp中,對該命令的處理如下:

status_t IPCThreadState::executeCommand(int32_t cmd){    BBinder* obj;    RefBase::weakref_type* refs;    status_t result = NO_ERROR;      switch (cmd) {  ...      case BR_ACQUIRE:          refs = (RefBase::weakref_type*)mIn.readInt32();          obj = (BBinder*)mIn.readInt32();          ...          obj->incStrong(mProcess.get());          ...          mOut.writeInt32(BC_ACQUIRE_DONE);          mOut.writeInt32((int32_t)refs);          mOut.writeInt32((int32_t)obj);          break;...

接着Binder驅動繼續處理BC_ACQUIRE_DONE命令:

在確認傳進來的參數OK後,會置pending_weak_ref0,代表現在用戶空間與驅動對結點的引用計數都處理完成。爲什麼要通知用戶空間也要修改引用計數呢?這是因爲驅動裏面將用戶空間傳進來的RefBase::weakref_type實例的內存地址值作爲binder_node結點在紅黑樹上排序的依據,並通過這個地址值從紅黑樹上查找對應的binder_node。所以,必須保證用戶空間對應的RefBase::weakref_type實例在結點存活期間不會因爲引用計數爲零被銷毀,導致binder_node成爲無法索引的結點。另外,cookie標識當前會話,也必須保證其對應的本地BBinder對象在binder存活期間不會被析構。

 

13. has_weak_ref:是否拥有弱引用。

14. pending_weak_ref:对结点的弱引用是否处理完成(BC_INCREFS_DONE

15. has_async_transaction:拥有异步事务?

16. accept_fds:是否允许传递文件描述符?

17. min_priority:最低优先级

18. async_todo:该结点上的异步工作队列。


新结点的创建

在函数binder_new_node(),创建的新结点将会加入到当前进程的结点树中,这些结点通过红黑树的结构来维护,以本地Binder对象的内存地址的大小作为排序依据。此函数主要是在BR_TRANSACTION中,为发送方创建相应的本地结点,以便接收方能够通过该结点获取关于发送方的一些信息。当然,在调用BINDER_SET_CONTEXT_MGR命令时,也会调用该函数创建一个特殊的结点。

 

结点引用计数管理

结点引用计数的管理主要通过如下两个函数:

static intbinder_inc_node(struct binder_node *node, int strong, int internal,

                        struct list_head *target_list)

static intbinder_dec_node(struct binder_node *node, int strong, int internal)


结点引用

结点引用与结点密不可分。事实上,可以认为结点引用是结点在另一个进程中的代理。它们之间的关系是多对一的关系,即一个结点可以对应多个结点引用,但是结点本身只能存在于一个进程中,且结点引用与结点一般是属于不同进程的。它的数据结构定义如下:


struct binder_ref {       /* Lookups needed: */       /*   node + proc => ref (transaction) */       /*   desc + proc => ref (transaction, inc/dec ref) */       /*   node => refs + procs (proc exit) */       int debug_id;       struct rb_node rb_node_desc;       struct rb_node rb_node_node;       struct hlist_node node_entry;       struct binder_proc *proc;       struct binder_node *node;       uint32_t desc;       int strong;       int weak;       struct binder_ref_death *death;};

下面分别解释上述各个成员变量的意义:

1. debug_id:同结点的debug_id意义一样,一个数据序号标记。

2. rb_node_desc:代表binder_proc中红黑树refs_by_desc中的一个结点,以desc为索引,即结点引用的句柄号作为排序依据。

3. rb_node_node:代表binder_proc中红黑树refs_by_node中的一个结点,以node为索引,即结点的内存地址作为排序依据。

4. node_entry:作为node所拥有的结点引用链表中的一个结点

5. proc:该结点引用相关联的binder_proc

6. node: 该结点引用所关联的结点

7. desc:该结点引用的句柄号

8. strong: 该结点引用的强引用计数

9. weak: 该结点引用的弱引用计数

10. death:该结点引用的死亡通知链表,主要通知它所引用的结点的死亡消息。

 

系统中所有的Binder实体以及每个实体在各个进程中的引用都登记在驱动中;驱动需要记录Binder引用 ->实体之间多对一的关系;为引用找到对应的实体;在某个进程中为实体创建或查找到对应的引用;记录Binder的归属地(位于哪个进程中);

函数static struct binder_ref binder_get_ref(struct binder_proc *proc, uint32_tdesc)用于查询某个句柄号为descstruct binder_ref对象,而函数

static structbinder_ref *binder_get_ref_for_node(struct binder_proc *proc,

struct binder_node *node)

则是为某个结点对象创建一个结点引用对象。


结点引用计数管理

结点引用计数管理主要通过如下两个函数:

static int binder_inc_ref(struct binder_ref *ref, int strong,

                       struct list_head *target_list)

static int binder_dec_ref(struct binder_ref *ref, int strong)

 

 

 

binder_inc_ref里面调用binder_inc_nodeinternal1,直接调用binder_inc_node时,internal0

所以,node->internal_strong_refs统计的是binder_ref对远程结点的强引用。对结点本身来说,node->internal_strong_refs的值也反映了当前有多少个远程结点(binder_ref)强引用指向自己。

node->local_strong_refs则统计的是对本地结点的强引用,这个值反映了当前进程中有多少个强引用指向自己。

 

Binder驱动对结点引用计数的管理

第一种情况,用户空间可以通过如下一个命令来增加或减少结点的引用计数:

1. BC_INCREFS 

2. BC_ACQUIRE 

3. BC_RELEASE

4. BC_DECREFS

 

IPCThreadState类中定义了如下几个相关接口:

incStrongHandle(int32_t handle)

incWeakHandle(int32_t handle)

decStrongHandle(int32_t handle)

decWeakHandle(int32_t handle)

分别会向驱动发送上述几个命令。

这个命令带的参数是结点的句柄号。 这种方法是直接改变结点的引用计数。在驱动中也可能直接改变结点的引用计数,

binder_inc_node(target_node, 1, 0, NULL),有一个共同点是target_list参数都为NULL

 

第二种情况是,在处理TRANSACTION期间,Binder驱动改变了传输中的结点引用计数,然后通过如下几个命令返回给用户空间做处理:

1. BR_ACQUIRE

2. BR_INCREFS

3. BR_RELEASE

4. BR_DECREFS

 

其中,当创建本进程中的某个结点的结点引用对象时,需要传入一个target_list参数,提交一个BINDER_WORK_NODE类型的binder_work,以处理驱动中的结点引用计数管理,同时,通过上述几个命令通知用户空间维护相对应的对象强弱引用计数。

binder_inc_ref(ref, fp->type ==BINDER_TYPE_HANDLE,

                                   &thread->todo);



0 0