Binder—Binder 对象生命周期
一、注册服务流程
1.1 图片形式
1.2 文字形式
注册服务,详细步骤如下:
- 注册服务进程发送 BC_TRANSACTION 协议给 Binder 驱动程序 // 调用的是 binder_thread_write 函数,向 Binder 驱动程序写数据
调用 binder_transaction(),当前是请求阶段
// 因为当前跨进程请求目的是为了注册服务,参数肯定不能只有服务名,那再传个什么参数才能代表注册了个服务呢?比如传个指针什么的,可以表示服务的唯一性,事实上,传了一个 type 为 BINDER_TYPE_BINDER 的 Binder 对象,成员 binder 引用了 ptr,这个 ptr 好像就是服务相关的引用(待验证)
① 通信对象是 ServiceManager,handle 为 0,当前调用 binder_inc_node 增加对 ServiceManager 这个 Binder 实体的引用计数
② 请求数据包含 Binder 对象,开始解析 Binder 数据的流程,根据 Binder 的 type 为 BINDER_TYPE_BINDER
调用 binder_get_node 获取属于注册服务进程的 Binder 实体对象(存在就返回,不存在返回 NULL,首次获取时返回 NULL),如果返回 NULL,则调用 binder_new_node 创建属于注册服务进程的 Binder 实体对象(创建 Binder 实体对象过程中,会添加一个类型为 BINDER_WORK_NODE 的事务)
调用 binder_get_ref_for_node 去返回一个属于目标进程 ServiceManager 的 binder_ref(不存在就创建,之后就返回已创建的)
修改 Binder 对象的 type,由 BINDER_TYPE_BINDER 改成 BINDER_TYPE_HANDLE
设置服务句柄 handle 值,赋值为 binder_get_ref_for_node 返回的 binder_ref 对象的 desc 值(可以看成服务注册的顺序)
调用 binder_inc_ref,分情况:首次注册服务:会增加 Binder 实体对象的引用计数,非首次注册服务,增加 Binder 引用对象的引用计数(也就是说,只有第一次才真正注册服务,后面的只是增加 Binder 引用对象的引用)
③ Binder 驱动程序往目标进程 ServiceManager 进程的 todo 队列塞了一个类型为 BINDER_WORK_TRANSACTION 的事务,唤醒 ServiceManager 进程
ServiceManager 被唤醒后,收到 Binder 驱动程序发来的事务 - ServiceManager 处理 Binder 驱动程序中转的注册服务的请求
① 取出事务
第一次取出的是类型为 BINDER_WORK_NODE 的事务,该类型事务与 Binder 实体对象相关,需要修改 Binder 实体对象对应的 Binder 本地对象的引用计数,通过 put_user 把协议(BR_ACQUIRE、BR_RELEASE、BR_INCREFS、BR_DECREFS)及内容写入 Server 进程用户空间缓冲区,然后 Server 进程收到数据,完成相应请求,如果是请求处理的协议是用来增加 Binder 本地对象的强弱引用的(BR_ACQUIRE、BR_INCREFS),则立马通过协议(BC_ACQUIRE_DONE、BC_INCREFS_DONE)通知 Binder 驱动程序,已增加 Binder 本地对象的强弱引用计数
第二次取出的是类型为 BINDER_WORK_TRANSACTION 的事务,将 cmd 改为 BR_TRANSACTION,然后把注册服务的数据通过 put_user、copy_to_user 写到用户空间
// 下面的 ②③④⑤⑥⑦ 都在 ServiceManager 进程的用户空间
② 先根据注册服务的数据中的 code 执行相应函数,当前就是注册服务
③ 调用 bio_get_string16 获取要注册的服务名
④ 调用 bio_get_ref 获取服务的引用号 handle(Binder 驱动程序调用 binder_get_ref_for_node 创建引用对象时生成)
⑤ 调用 do_add_service 在应用程序层面注册服务(实际为记录服务名和 handle),如果当前 ServiceManager 进程不存在要注册的服务(首次注册服务),则先创建一个用来描述要注册的服务的 struct svcinfo 结构体对象,然后用注册服务的相关数据给这个结构体对象赋值,再把这个结构体对象添加到链表 svcinfo 中。如果当前 ServiceManager 进程存在要注册的服务(重复注册服务),则更新服务的 handle 值
⑥ 若 do_add_service 注册服务成功,则调用 bio_put_uint32(reply, 0),将用于通知注册服务进程注册服务成功
⑦ 调用 binder_send_reply 通过 BC_REPLY 协议返回 reply(0) 给注册服务进程表示注册服务成功(需要经过 Binder 驱动程序中转) - ServiceManager 进程发送 BC_REPLY 协议给 Binder 驱动程序 // 调用的是 binder_thread_write 函数,向 Binder 驱动程序写数据
调用 binder_transaction(),当前是回复阶段(reply)
// 当前返回的仅仅是 uint32 类型的 0,即 00 00 00 00,因为携带的数据不含 Binder 对象,则
① 返回数据不含 Binder 对象,跳过解析 Binder 的过程
② Binder 驱动程序往目标进程注册服务进程的 todo 队列塞了一个类型为 BINDER_WORK_TRANSACTION 的事务,唤醒注册服务进程
注册服务进程被唤醒后,收到 Binder 驱动程序发来的新事务 - 注册服务进程处理 Binder 驱动程序中转的回复数据
① 取出类型为 BINDER_WORK_TRANSACTION 的事务,将 cmd 改为 BR_REPLY,然后把回复数据通过 put_user 、、copy_to_user 写到用户空间
// 下面的 ② 都在注册服务进程的用户空间
② 此时注册服务进程的用户空间中的 reply 就有值了(reply 中仅包含 00 00 00 00),表示注册服务成功,然后就结束了
1.3 注册服务整个流程,涉及 Binder 对象生命周期的地方,概括如下:
- 先要获取 ServiceManager 的代理对象(当前进程第一次获取会创建再返回,后续返回已创建的对象),增加 ServiceManager 这个 Binder 实体的引用计数
- 调用 binder_get_node 获取属于注册服务进程的 Binder 实体对象(存在就返回,不存在返回 NULL),如果返回 NULL,则调用 binder_new_node 创建属于注册服务进程的 Binder 实体对象(创建 Binder 实体对象过程中,会添加一个类型为 BINDER_WORK_NODE 的事务)
// ServiceManager 处理类型为 BINDER_WORK_NODE 的事务,该类型事务与 Binder 实体对象相关,需要修改 Binder 实体对象对应的 Binder 本地对象的引用计数 - 调用 binder_get_ref_for_node 获取属于目标进程 ServiceManager 的 binder_ref(不存在就创建,之后就返回已创建的)
- 调用 binder_inc_ref,分情况:首次注册服务:会增加 Binder 实体对象的引用计数,非首次注册服务,增加 Binder 引用对象的引用计数(也就是说,只有第一次才真正注册服务,后面的只是增加 Binder 引用对象的引用)
二、获取服务流程
2.1 图片形式
2.2 文字形式
通过服务名获取服务,详细步骤如下:
- 获取服务进程发送 BC_TRANSACTION 协议给 Binder 驱动程序 // 调用的是 binder_thread_write 函数,向 Binder 驱动程序写数据
调用 binder_transaction(),当前是请求阶段
// 因为当前跨进程请求目的是为了获取一个 Binder 代理对象,很明显携带的请求数据不含 Binder 对象(只有服务名),则
① 通信对象是 ServiceManager,handle 为 0,当前调用 binder_inc_node 增加对 ServiceManager 这个 Binder 实体的引用计数
② 请求数据不含 Binder 对象,跳过解析 Binder 的过程
③ Binder 驱动程序往目标进程 ServiceManager 进程的 todo 队列塞了一个事务,唤醒 ServiceManager 进程
ServiceManager 被唤醒后,收到 Binder 驱动程序发来的新事务 - ServiceManager 进程处理 Binder 驱动程序中转的获取服务的请求
① 取出类型为 BINDER_WORK_TRANSACTION 的事务,将 cmd 改为 BR_TRANSACTION,然后把获取服务的数据通过 put_user、copy_to_user 写到用户空间
// 下面的 ②③④ 都在 ServiceManager 进程的用户空间
② 先根据获取服务的数据执行对应函数,当前就是根据服务名获取服务句柄
③ 调用 bio_put_ref 生成一个 Binder 对象,type 类型为 BINDER_TYPE_HANDLE
④ 调用 binder_send_reply 通过 BC_REPLY 协议返回服务句柄给获取服务进程(需要经过 Binder 驱动程序中转) - ServiceManager 进程发送 BC_REPLY 协议给 Binder 驱动程序 // 调用的是 binder_thread_write 函数,向 Binder 驱动程序写数据
调用 binder_transaction(),当前是回复阶段(reply)
// 因为当前跨进程请求目的是返回一个 Binder 代理对象,很明显携带的请求数据含 Binder 对象,则
① 回复数据含 Binder 对象,解析返回数据中的 Binder 对象,因为获取服务的进程是客户端进程,不是服务端进程
调用 binder_get_ref 根据返回数据中的 Binder 对象服务句柄返回属于获取服务进程的 binder_ref
调用 binder_get_ref_for_node 根据 binder_ref->node 去返回一个属于目标进程 ServiceManager 的 binder_ref(不存在就创建,之后就返回已创建的)
调用 binder_inc_ref,分情况:首次获取服务:会增加 Binder 实体对象的引用计数,非首次获取服务:增加 Binder 引用对象的引用计数
② Binder 驱动程序往目标进程获取服务进程的 todo 队列塞了一个类型为 BINDER_WORK_TRANSACTION 的事务,唤醒获取服务进程
获取服务进程被唤醒后,收到 Binder 驱动程序发来的新事务 - 获取服务进程处理 Binder 驱动程序中转的获取服务的请求
① 取出事务,把请求数据通过 put_user、copy_to_user 写到用户空间,然后将 cmd 改为 BR_REPLY
// 下面的 ②③④ 都在获取服务进程的用户空间
② 此时获取服务进程的用户空间中的 reply 就有值了(reply 中包含了一个 Binder 代理对象,里面含有服务的句柄值,注意,这里说的代理是说 type 为 BINDER_TYPE_HANDLE 的意思,不是 BpBinder)
此时,获取服务进程在用户空间通过 getStrongProxyForHandle(Binder 代理对象->服务句柄) 返回一个 BpBinder 对象(第一次创建后返回,之后直接返回已创建的),如果是第一次创建的 BpBinder 对象,此时会发送 BC_INCREFS 协议,增加相应的 Binder 引用对象的弱引用计数
2.3 获取服务整个流程,涉及 Binder 对象生命周期的地方,概括如下:
- 先要获取 ServiceManager 的代理对象(当前进程第一次获取会创建再返回,后续返回已创建的对象),增加 ServiceManager 这个 Binder 实体的引用计数
- 生成 type 为 BINDER_TYPE_HANDLE 的 Binder 对象,根据里面的句柄调用 binder_get_ref 找到 binder_ref,再根据 binder_ref->node 调用 binder_get_ref_for_node 去返回一个 binder_ref(不存在就创建,之后就返回已创建的),然后调用 binder_inc_ref 去增加 Binder 实体对象的强引用计数(获取服务进程第一次获取服务,因此这个 binder_ref 是新创建的,这个 Binder 引用对象对 Binder 实体对象的强引用为 0,通过 binder_inc_node 增加实体对象引用,避免实体对象还被引用对象引用时,实体对象被销毁)或增加 Binder 引用对象的引用计数(获取服务进程不是第一次获取服务,使用已创建的 binder_ref,然后增加 Binder 引用对象的引用计数)
- 将 type 为 BINDER_TYPE_HANDLE 的 Binder 对象返回给获取服务进程,取出里面的服务句柄,返回一个 BpBinder 对象(首次创建 BpBinder:会增加相应的 Binder 引用对象的弱引用计数 + 非首次创建 BpBinder 对象:直接使用该对象)
2.4 获取服务过程,涉及 Binder 对象声明周期,时间路线:
- BC_REPLY 阶段
首次获取服务:会增加 Binder 实体对象的强引用计数
非首次获取服务:增加 Binder 引用对象的引用计数(判断是否是首次获取服务的标准是,能不能根据服务名查到的服务句柄查到的服务引用查到的服务实体,看看获取服务进程的服务引用红黑树上是否存在一个节点对应的 binder_ref 所引用的 binder_node 和前面查到的服务实体相等) - BR_REPLY 阶段,首次创建 BpBinder:会增加相应的 Binder 引用对象的弱引用计数 + 非首次创建 BpBinder 对象:直接使用该对象(判断是否需要首次创建 BpBinder 的标准是,能不能根据返回的服务句柄作为关键字从内部的 Binder 代理队列中查找到一个 handle_entry 结构体,若找到则使用 handle_entry 结构体内部的 IBinder 指针指向的 BpBinder 对象,没找到则需要先创建,然后再保存在 handle_entry 中)
三、BpBinder 对象存在直接返回已创建的,从代码角度看体现在哪?
因为上面 “获取服务流程” 最后一段说 “获取服务进程在用户空间通过 getStrongProxyForHandle(Binder 代理对象->服务句柄) 返回一个 BpBinder 对象(第一次创建后返回,之后直接返回已创建的)”,自然想到去查看 getStrongProxyForHandle 函数实现
[frameworks/native/libs/binder/ProcessState.cpp, getStrongProxyForHandle()]
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
// 找到了 handle_entry 藏身之处
// 根据服务句柄 handle 查找对应的资源项 handle_entry,若 lookupHandleLocked() 函数没有对应的资源项,则会创建一个新项 handle_entry 返回(但是成员 binder 为 NULL),这个新项 handle_entry 的内容需要填充
// 看了很多文章,都说如果找到了服务句柄 handle 对应的资源项 handle_entry,最后直接返回 BpBinder 对象(但当前还未完全理清这一点内容,待完善)
handle_entry* e = lookupHandleLocked(handle);
// 上面刚创建完 handle_entry,肯定不为 NULL
if (e != NULL) {
// 成员 binder 为 NULL
IBinder* b = e->binder;
// 满足 if 第一个条件:binder 为 NULL
if (b == NULL || !e->refs->attemptIncWeak(this)) {
// 当前的服务句柄值 handle 对应的是 Server 服务,当前 if 不满足
if (handle == 0) {
Parcel data;
status_t status = IPCThreadState::self()->transact(
0, IBinder::PING_TRANSACTION, data, NULL, 0);
if (status == DEAD_OBJECT)
return NULL;
}
// 根据服务句柄 handle 创建 BpBinder 对象
b = new BpBinder(handle);
// 上面不是说 handle_entry 的成员 binder 为 NULL 吗,在此用新创建的 BpBinder 对象赋完值就不为 NULL 了
// 也说明了,下次再获取相同服务句柄值 handle 时,lookupHandleLocked() 函数返回的 handle_entry 的成员 binder 就不为 NULL 了,也就不需要重复创建 BpBinder 对象了
e->binder = b;
if (b) e->refs = b->getWeakRefs();
// result 赋值为 BpBinder 对象
result = b;
} else {
result.force_set(b);
e->refs->decWeak(this);
}
}
// 返回 BpBinder 对象
return result;
}
[frameworks/native/libs/binder/ProcessState.cpp, lookupHandleLocked()]
// 根据索引查找对应的资源项,没有则创建新的资源项返回,新的资源项之后需要填充
ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
// mHandleToObject:Vector<handle_entry>,本进程中记录所有 BpBinder 的向量表
const size_t N = mHandleToObject.size();
if (N <= (size_t)handle) {
// struct handle_entry {
// IBinder* binder;
// RefBase::weakref_type* refs;
// };
handle_entry e;
e.binder = NULL; // 成员 binder 设置为 NULL
e.refs = NULL; // 成员 refs 设置为 NULL
status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
if (err < NO_ERROR) return NULL;
}
// 返回 handle 对应资源的地址(返回的时成员 binder 还为 NULL)
return &mHandleToObject.editItemAt(handle);
}
四、一个进程注册多个 Binder 服务,如何区分
提出猜测
当前跨进程请求目的是为了注册服务,参数肯定不能只有服务名,那再传个什么参数才能代表注册了个服务呢?比如传个指针什么的,可以表示服务的唯一性,事实上,传了一个 type 为 BINDER_TYPE_BINDER 的 Binder 对象,成员 binder 引用了 ptr,这个 ptr 应该就是服务相关的引用,下面验证该猜测
验证猜测
当前标题和挖坑待验证说的是一个意思,就是说一个进程可以注册多个 Binder 服务,在使用 Binder 服务时,当提供服务进程收到调用服务请求后,提供服务进程如何确定用哪个 Binder 服务去响应调用服务请求
现在开始验证(需要提前说明的是,当前讨论的前提是 C++ 层面的注册服务),从下面三个角度分析(重点关注注册服务谁代表了注册的服务,至于注册服务的整个流程有空再说)
- 注册服务进程(注册服务)
- Binder 驱动程序(中转站)
- 获取服务进程(使用服务)
4.1 注册服务进程(注册服务)
构造数据,注册服务
[–>bctest.c, svcmgr_publish()]
// 注意最后一个参数 ptr(是一个函数指针,用于响应服务请求),该参数可以用代表注册的服务
int svcmgr_publish(struct binder_state *bs, uint32_t target, const char *name, void *ptr)
{
int status;
unsigned iodata[512/4];
struct binder_io msg, reply;
// 构造数据
bio_init(&msg, iodata, sizeof(iodata), 4);
bio_put_uint32(&msg, 0); // strict mode header
bio_put_string16_x(&msg, SVC_MGR_NAME);
// 指定要注册的服务名称
bio_put_string16_x(&msg, name);
// 查看 bio_put_obj(),注意这个 ptr,代表了服务
bio_put_obj(&msg, ptr);
// 向驱动程序发送数据
if (binder_call(bs, &msg, &reply, target, SVC_MGR_ADD_SERVICE))
return -1;
status = bio_get_uint32(&reply);
binder_done(bs, &msg, &reply);
return status;
}
[–>binder.c, bio_put_obj()]
void bio_put_obj(struct binder_io *bio, void *ptr)
{
// struct flat_binder_object 可以用来描述一个 Binder 对象
struct flat_binder_object *obj;
obj = bio_alloc_obj(bio);
if (!obj)
return;
obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
// BINDER_TYPE_BINDER 表示当前是一个 Binder 实体对象
obj->type = BINDER_TYPE_BINDER;
// 注意传入的 ptr 赋给了 flat_binder_object 的 binder 成员
obj->binder = (uintptr_t)ptr;
obj->cookie = 0;
}
构造的数据达到 Binder 驱动程序,准备解析收到的数据
4.2 Binder 驱动程序(中转站)
解析数据,处理数据
[–>kernel/drivers/android/binder.c, binder_transaction()]
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply)
{
struct binder_transaction *t;
struct binder_work *tcomplete;
size_t *offp, *off_end;
struct binder_proc *target_proc;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
struct list_head *target_list;
wait_queue_head_t *target_wait;
struct binder_transaction *in_reply_to = NULL;
uint32_t return_error = BR_OK;
...
// 创建 binder_transaction 结构体
t = kzalloc(sizeof(*t), GFP_KERNEL);
if (t == NULL) {
return_error = BR_FAILED_REPLY;
goto err_alloc_t_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION);
// 创建 binder_work 结构体
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
if (tcomplete == NULL) {
return_error = BR_FAILED_REPLY;
goto err_alloc_tcomplete_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;
else
t->from = NULL;
t->sender_euid = proc->tsk->cred->euid;
t->to_proc = target_proc;
t->to_thread = target_thread;
t->code = tr->code;
t->flags = tr->flags;
t->priority = task_nice(current);
// t->buffer 是从目的进程 target_proc(目的进程是 Service Manager 进程)所映射的空间映射的空间
t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
if (t->buffer == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_alloc_buf_failed;
}
t->buffer->allow_user_free = 0;
t->buffer->debug_id = t->debug_id;
t->buffer->transaction = t;
// 当前目的节点 target_node 为 binder_context_mgr_node(请求阶段有值,回复阶段没有值)
t->buffer->target_node = target_node;
if (target_node)
binder_inc_node(target_node, 1, 0, NULL);
// offp:数据缓冲区起始地址
offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
// 下面两个 if 从注册服务进程的用户空间拷贝数据到目标进程 target_proc(目的进程是 Service Manager 进程)的内核空间
// t->buffer 是从目的进程 target_proc(目的进程是 Service Manager 进程)所映射的空间映射的空间
// tr->data.ptr.buffer:binder_transaction_data 数据缓冲区中有一个 type 为 BINDER_TYPE_BINDER 的 Binder 对象
if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
// tr->data.ptr.offsets:binder_transaction_data 偏移数组记录了 Binder 对象在数据缓冲区中的位置
if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
// offsets_size:偏移数组大小
off_end = (void *)offp + tr->offsets_size;
// for 循环遍历数据缓冲区中的 Binder 对象,遍历范围 [offp, off_end]
for (; offp < off_end; offp++) {
struct flat_binder_object *fp;
// 根据数据缓冲区起始位置 + 偏移确定一个 Binder 对象,赋给 fp(flat_binder_object)
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
switch (fp->type) {
case BINDER_TYPE_BINDER: // BINDER 实体,走这个
case BINDER_TYPE_WEAK_BINDER: {
struct binder_ref *ref;
// 在内核态获取一个 binder_node 节点给进程 proc(当前进程是注册服务进程)
struct binder_node *node = binder_get_node(proc, fp->binder);
if (node == NULL) {
// 查看 binder_new_node(),第一次获取为空,则创建 binder_node 节点
node = binder_new_node(proc, fp->binder, fp->cookie);
if (node == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_new_node_failed;
}
node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
}
// 在内核态构造一个 binder_ref 节点给目的进程 target_proc(目的进程是 Service Manager 进程)
ref = binder_get_ref_for_node(target_proc, node); // 引用会从 1 开始递增,引用 0 用来表示引用 Service Manager
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; // type 变为 BINDER_TYPE_HANDLE,因为当前为 Service Manager 进程,只能使用 struct binder_ref
else
fp->type = BINDER_TYPE_WEAK_HANDLE;
// handle 从 1 开始增长,代表第几个引用
fp->handle = ref->desc; // 对于 struct binder_ref,不能使用 binder,需要使用 handle,赋值为 ref->desc
// 增加 struct binder_ref 的引用计数,同时会返回一些信息给 thread(当前为 Server 中对应的线程),告诉该 thread 有别的线程引用自己
binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo);
} break;
...
}
}
...
// 设置 t(binder_transaction 结构体)任务类型为 BINDER_WORK_TRANSACTION
t->work.type = BINDER_WORK_TRANSACTION;
// 将 t(binder_transaction 结构体)添加到目标线程 todo 队列 target_list 中
list_add_tail(&t->work.entry, target_list);
// 设置 tcomplete(binder_work 结构体)任务类型为 BINDER_WORK_TRANSACTION_COMPLETE
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
// 将 tcomplete(binder_work 结构体)添加到 ServiceManager 进程主线程的 todo 队列 中
list_add_tail(&tcomplete->entry, &thread->todo);
// 目标进程(Service Manager 进程)处于睡眠中,此时唤醒目标进程(Service Manager 进程)
if (target_wait)
wake_up_interruptible(target_wait);
return;
...
}
在上个阶段,我们构造了一个 struct flat_binder_object
- type = BINDER_TYPE_BINDER // Binder 实体对象
- binder = (uintptr_t)ptr // ptr 赋给了 flat_binder_object 的 binder 成员
上面的方法中,后面的那个 for 循环,就是用来解析我们构造的 Binder 对象,因为我们指定的 type 是 BINDER_TYPE_BINDER,则会执行 binder_new_node()
函数,用于在内核中为注册服务进程的 Binder 实体对象创建一个 binder_node 节点
// for 循环遍历数据缓冲区中的 Binder 对象
for (; offp < off_end; offp++) {
struct flat_binder_object *fp;
// 解析出 struct flat_binder_object
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
switch (fp->type) {
case BINDER_TYPE_BINDER: // BINDER 实体,走这个
case BINDER_TYPE_WEAK_BINDER: {
struct binder_ref *ref;
// 在内核态获取一个 binder_node 节点给进程 proc(当前进程是注册服务进程)
struct binder_node *node = binder_get_node(proc, fp->binder);
if (node == NULL) {
// 查看 binder_new_node(),第一次获取为空,则创建 binder_node 节点
// 注意第二个参数 fp->binder,这是 flat_binder_object 的 binder 成员啊
node = binder_new_node(proc, fp->binder, fp->cookie);
...
// 第二个参数 ptr 在上面调用时传入的是 flat_binder_object 的 binder 成员
static struct binder_node *binder_new_node(struct binder_proc *proc,
void __user *ptr,
void __user *cookie)
{
struct rb_node **p = &proc->nodes.rb_node;
struct rb_node *parent = NULL;
struct binder_node *node;
while (*p) {
parent = *p;
node = rb_entry(parent, struct binder_node, rb_node);
if (ptr < node->ptr)
p = &(*p)->rb_left;
else if (ptr > node->ptr)
p = &(*p)->rb_right;
else
return NULL;
}
node = kzalloc(sizeof(*node), GFP_KERNEL);
if(node == NULL)
return NULL;
binder_stats_created(BINDER_STAT_NODE);
rb_link_node(&node->rb_node, parent, p);
rb_insert_color(&node->rb_node, &proc->nodes);
node->debug_id = ++binder_last_id;
node->proc = proc;
// 把 ptr 赋给了 binder_node 的 ptr
node->ptr = ptr;
node->cookie = cookie;
node->work.type = BINDER_WORK_NODE;
INIT_LIST_HEAD(&node->work.entry);
INIT_LIST_HEAD(&node->async_todo);
return node;
}
ptr 一开始赋给了 flat_binder_object 的 binder 成员,最终保存在了内核为注册服务进程创建的 binder_node 的 ptr 成员中
4.3 获取服务进程(使用服务)
假设已完成获取服务过程,然后根据获取的服务 handle 去使用服务
[–>kernel/drivers/android/binder.c, binder_transaction()]
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply)
{
struct binder_transaction *t;
struct binder_work *tcomplete;
size_t *offp, *off_end;
struct binder_proc *target_proc;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
struct list_head *target_list;
wait_queue_head_t *target_wait;
struct binder_transaction *in_reply_to = NULL;
uint32_t return_error = BR_OK;
if (reply) {
...
} else { // reply 为 false,走这个
if (tr->target.handle) { // 上面我们假定获取了服务,拿到了 handle(不为 0),走这个
struct binder_ref *ref;
// 根据服务 handle 获取 binder_ref
ref = binder_get_ref(proc, tr->target.handle);
if (ref == NULL) {
return_error = BR_FAILED_REPLY;
goto err_invalid_target_handle;
}
// 获取 binder_node
target_node = ref->node;
} else { // 目的进程 ServerManager,值为 0
target_node = binder_context_mgr_node;
if (target_node == NULL) {
return_error = BR_DEAD_REPLY;
goto err_no_context_mgr_node;
}
}
e->to_node = target_node->debug_id;
target_proc = target_node->proc; // 提供服务进程
if (target_proc == NULL) {
return_error = BR_DEAD_REPLY;
goto err_dead_binder;
}
// 首次请求,获取服务的线程 thread 事务堆栈 transaction_stack 为空
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
struct binder_transaction *tmp;
tmp = thread->transaction_stack;
if (tmp->to_thread != thread) {
return_error = BR_FAILED_REPLY;
goto err_bad_call_stack;
}
while (tmp) {
if (tmp->from && tmp->from->proc == target_proc)
target_thread = tmp->from;
tmp = tmp->from_parent;
}
}
}
if (target_thread) {
target_list = &target_thread->todo;
target_wait = &target_thread->wait;
} else { // 目标线程为空,走这个
// 获取目标进程 todo 队列
target_list = &target_proc->todo;
// 获取目标进程 wait 等待队列
target_wait = &target_proc->wait;
}
// 创建 binder_transaction 结构体,用来记录一次单向传输过程
t = kzalloc(sizeof(*t), GFP_KERNEL);
if (t == NULL) {
return_error = BR_FAILED_REPLY;
goto err_alloc_t_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION);
// 创建 binder_work 结构体
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
if (tcomplete == NULL) {
return_error = BR_FAILED_REPLY;
goto err_alloc_tcomplete_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
// 不是回复阶段 && 需要回复,当前是请求阶段,需要回复,
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread; // 获取服务的线程
else
t->from = NULL;
t->sender_euid = proc->tsk->cred->euid;
t->to_proc = target_proc; // 提供服务进程
t->to_thread = target_thread; // 为 NULL
t->code = tr->code;
t->flags = tr->flags;
t->priority = task_nice(current);
t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
if (t->buffer == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_alloc_buf_failed;
}
t->buffer->allow_user_free = 0;
t->buffer->debug_id = t->debug_id;
t->buffer->transaction = t;
// 当前目的节点 target_node 为根据服务 handle 找到的 binder_node(请求阶段有值,回复阶段没有值)
t->buffer->target_node = target_node;
if (target_node)
binder_inc_node(target_node, 1, 0, NULL);
// offp:数据缓冲区起始位置
offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
// 下面两个 if 从用户空间拷贝数据到内核空间
// tr->data.ptr.buffer:binder_transaction_data 数据缓冲区中只包含要访问的服务名称
if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
// tr->data.ptr.offsets:binder_transaction_data 偏移数组记录了 Binder 对象在数据缓冲区中的位置(当前数据缓冲区中不含 Binder 对象)
if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
// offsets_size:偏移数组大小
off_end = (void *)offp + tr->offsets_size;
// 当前数据缓冲区中不含 Binder 对象,跳过 for 循环(若有 Binder 对象时,则遍历数据缓冲区中的 Binder 对象,遍历范围 [offp, off_end])
for (; offp < off_end; offp++) {
...
}
...
// 设置 t(binder_transaction 结构体)任务类型为 BINDER_WORK_TRANSACTION
t->work.type = BINDER_WORK_TRANSACTION;
// 将 t(binder_transaction 结构体)添加到目标线程 todo 队列 target_list 中
list_add_tail(&t->work.entry, target_list);
// 设置 tcomplete(binder_work 结构体)任务类型为 BINDER_WORK_TRANSACTION_COMPLETE
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
// 将 tcomplete(binder_work 结构体)添加到获取服务的线程的 todo 队列 中
list_add_tail(&tcomplete->entry, &thread->todo);
// 提供服务进程处于睡眠中,此时唤醒提供服务进程
if (target_wait)
wake_up_interruptible(target_wait);
return;
...
}
当前根据服务 handle 找到对应的 binder_node,然后 ServiceManager 进程被唤醒,处理任务类型为 BINDER_WORK_TRANSACTION 的事务
[–>kernel/drivers/android/binder.c, binder_thread_read()]
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
int ret = 0;
int wait_for_proc_work;
if (*consumed == 0) {
// 将 BR_NOOP 写回到用户空间
if (put_user(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
}
// while 循环用于处理进程或线程 todo 队列中存在的未处理工作项
while (1) {
uint32_t cmd;
struct binder_transaction_data tr;
struct binder_work *w;
struct binder_transaction *t = NULL;
if (!list_empty(&thread->todo))
w = list_first_entry(&thread->todo, struct binder_work, entry);
else if (!list_empty(&proc->todo) && wait_for_proc_work) // 当前目标进程 ServiceManager 的 todo 队列不为空
// 取出工作项 binder_work
w = list_first_entry(&proc->todo, struct binder_work, entry);
else {
if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */
goto retry;
break;
}
if (end - ptr < sizeof(tr) + 4)
break;
switch (w->type) {
case BINDER_WORK_TRANSACTION: { // 处理任务类型为 BINDER_WORK_TRANSACTION 的事务
// 根据 binder_work 构造出 binder_transaction
t = container_of(w, struct binder_transaction, work);
} break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
...
} break;
}
// 收到 BINDER_WORK_TRANSACTION 时,t 不为空
if (!t)
continue;
// 请求阶段 target_node 不为空(请求阶段有值,回复阶段没有值),当前为根据服务 handle 找到对应的 binder_node
if (t->buffer->target_node) { // 当前是请求阶段,走这个
struct binder_node *target_node = t->buffer->target_node;
// 注意 target_node->ptr,这就是在注册服务时保存在 binder_node->ptr 中的,现在赋给了 binder_transaction_data.target.ptr
tr.target.ptr = target_node->ptr;
tr.cookie = target_node->cookie;
t->saved_priority = task_nice(current);
if (t->priority < target_node->min_priority && !(t->flags & TF_ONE_WAY))
binder_set_nice(t->priority);
else if (!(t->flags & TF_ONE_WAY) || t->saved_priority > target_node->min_priority)
binder_set_nice(target_node->min_priority);
cmd = BR_TRANSACTION; // BR_TRANSACTION
} else {
tr.target.ptr = NULL;
tr.cookie = NULL;
cmd = BR_REPLY;
}
tr.code = t->code;
tr.flags = t->flags;
tr.sender_euid = t->sender_euid;
if (t->from) {
struct task_struct *sender = t->from->proc->tsk;
tr.sender_pid = task_tgid_nr_ns(sender, current->nsproxy->pid_ns);
} else {
tr.sender_pid = 0;
}
tr.data_size = t->buffer->data_size;
tr.offsets_size = t->buffer->offsets_size;
// tr(binder_transaction_data)数据缓冲区中只包含要访问的服务名称
tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset;
tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *));
// 将 cmd = BR_TRANSACTION 写回到用户空间
if (put_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
// 将 tr(binder_transaction_data,数据缓冲区中只包含要访问的服务名称)写回到用户空间
if (copy_to_user(ptr, &tr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_stat_br(proc, thread, cmd);
list_del(&t->work.entry);
t->buffer->allow_user_free = 1;
// 当前是 BR_TRANSACTION && 需要回复
if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
t->to_parent = thread->transaction_stack; // to_parent 为 NULL
t->to_thread = thread;
// 将 t(binder_transaction 结构体)入栈(ServiceManager 进程主线程事务堆栈)
thread->transaction_stack = t;
} else {
t->buffer->transaction = NULL;
kfree(t);
binder_stats_deleted(BINDER_STAT_TRANSACTION);
}
break;
}
...
// 返回
return 0;
}
注册服务时保存在 binder_node->ptr 中的那个 ptr,现在赋给了 binder_transaction_data.target.ptr 成员
现在提供服务进程准备处理请求
[–>binder.c]
// binder_loop 读取请求后将解析这些请求,最后调用 binder_handler 完成最终的处理
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
uint32_t readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(uint32_t));
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
// 读取数据
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
break;
}
// 解析数据(在方法中会通过 func 回调函数,调用对应函数)
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
if (res == 0) {
break;
}
if (res < 0) {
break;
}
}
}
由上面的的代码注释可知,binder_parse()
通过 func 回调函数解析数据,fun 对应函数指针 binder_handler
已知注册服务时保存在 binder_node->ptr 中的那个 ptr,最终赋给了 binder_transaction_data.target.ptr 成员
[–>binder.h]
typedef int (*binder_handler)(struct binder_state *bs,
struct binder_transaction_data *txn, // 注意 binder_transaction_data
struct binder_io *msg,
struct binder_io *reply);
struct binder_transaction_data 用来描述进程间通信过程中传输的数据
struct binder_transaction_data {
union {
size_t handle;
void *ptr;
} target;
void *cookie;
unsigned int code;
unsigned int flags;
pid_t sender_pid;
uid_t sender_euid;
size_t data_size;
size_t offsets_size;
union {
struct {
const void *buffer;
const void *offsets;
} ptr;
uint8_t buf[8];
} data;
};
验证结论
于是,现在我们可以从 binder_transaction_data.target.ptr 成员中,还原出注册服务时保存在 binder_node->ptr 中的那个 ptr,所以可以说注册服务过程中,参数 ptr(其实是一个函数指针,用于响应服务请求)确实是可以用来代表注册的服务。因此,一开始我们在上面所做的猜测是对的