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_desc和refs_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_id是Binder驱动全局变量,且单调递增的。
2. work:一般类型为BINDER_WORK_NODE的binder_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_node,internal_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_node或binder_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_ref爲0,代表現在用戶空間與驅動對結點的引用計數都處理完成。爲什麼要通知用戶空間也要修改引用計數呢?這是因爲驅動裏面將用戶空間傳進來的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)用于查询某个句柄号为desc的struct 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_node,internal为1,直接调用binder_inc_node时,internal为0。
所以,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);
- Binder结点管理
- Binder结点死亡通知过程
- binder内核缓冲区管理
- Binder机制5--- Binder实现进程管理服务示例
- Binder机制5--- Binder实现进程管理服务示例
- Binder机制5--- Binder实现进程管理服务示例
- binder驱动-接收缓存区管理
- android binder 讲解之权限管理
- 从TransactionTooLargeException谈到binder的内存管理
- binder驱动-接收缓存区管理
- Android Binder 分析——内存管理
- binder
- binder
- binder
- Binder
- binder
- binder
- Binder
- 版本控制之最佳实践(Git版)
- HDU 1257 最少拦截系统
- php连接两个表然后查询数据代码,
- java 泛型
- HDU 1874 畅通工程续 (基础最短路)
- Binder结点管理
- python 调用图灵机器人api实现简单的人机交互
- 间谍飞哥点击返回家地方规划电工房电话发个电话发个
- 结果时间的官方的回复该电话费的规划法规电话发个
- Hdu 5072 Coprime(容斥)
- 日志相关
- 【设计原则】面向对象编程的六大原则
- android笔记
- leetcode - Minimum Window Substring