假设现在有一个客户端向sm请求MediaPlayer服务,本篇文章分析sm怎么返回服务的查询结果,即svcmgr_handler中怎么处理SVC_MGR_GET_SERVICE请求。
处理SVC_MGR_GET_SERVICE请求
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
//得到服务名, 这里应该是String16("media.player")
s = bio_get_string16(msg, &len);
if (s == NULL) {
return -1;
}
//从service list中找到该服务名对应的handle
handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
if (!handle)
break;
bio_put_ref(reply, handle);
return 0;
前面的博文已经分析了,handle值代表MediaPlayerService的binder_node对应的binder_ref的红黑树键值,这里从service list找到handle后,交给bio_put_ref处理。
bio_put_ref
void bio_put_ref(struct binder_io *bio, uint32_t handle)
{
struct flat_binder_object *obj;
if (handle)
obj = bio_alloc_obj(bio);
else
obj = bio_alloc(bio, sizeof(*obj));
if (!obj)
return;
obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
obj->type = BINDER_TYPE_HANDLE;
obj->handle = handle;
obj->cookie = 0;
}
根据handle值创建了一个flat_binder_object对象,类型为BINDER_TYPE_HANDLE,写入bio(即reply)的offs区域。
binder_send_reply
在上篇博文的binder_parse中我们知道,当调用完svcmgr_handler处理请求后,会调用binder_send_reply。
//binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
void binder_send_reply(struct binder_state *bs,
struct binder_io *reply,
binder_uintptr_t buffer_to_free,
int status)
{
struct {
uint32_t cmd_free;
binder_uintptr_t buffer;
uint32_t cmd_reply;
struct binder_transaction_data txn;
} __attribute__((packed)) data;
data.cmd_free = BC_FREE_BUFFER;//第一个协议
data.buffer = buffer_to_free;
data.cmd_reply = BC_REPLY;//第二个协议
data.txn.target.ptr = 0;
data.txn.cookie = 0;
data.txn.code = 0;
if (status) {
data.txn.flags = TF_STATUS_CODE;
data.txn.data_size = sizeof(int);
data.txn.offsets_size = 0;
data.txn.data.ptr.buffer = (uintptr_t)&status;
data.txn.data.ptr.offsets = 0;
} else {
data.txn.flags = 0;
data.txn.data_size = reply->data - reply->data0;
data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
}
binder_write(bs, &data, sizeof(data));
}
binder_send_reply构造了两个不同协议的数据并发送,第一个协议是BC_FREE_BUFFER,数据是本进程通信空间的地址,该空间曾被用于拷贝发送方发送的数据。由于binder_parse对数据的分析已经结束,所以这里发送BC_FREE_BUFFER协议的数据的目的应该释放这一空间。
第二个协议是BC_REPLY,数据部分是由binder_io reply填充而来的binder_transaction_data。reply中记录了handle信息。
binder_transaction分段1
前面已经分析过binder_transaction对BC_TRANSACTION的处理代码了,这里只分析binder_transaction对BC_REPLY的处理代码。
if (reply) {
binder_inner_proc_lock(proc);
//在上篇博文中,binder_thread_read的结尾会把读到的t放进sm的stack中,
//这里将其取出
in_reply_to = thread->transaction_stack;
if (in_reply_to == NULL) {
//错误处理...
}
if (in_reply_to->to_thread != thread) {
//错误处理...
}
//恢复之前的transaction_stack
thread->transaction_stack = in_reply_to->to_parent;
binder_inner_proc_unlock(proc);
//取出发送进程,也就是reply的目标进程
target_thread = binder_get_txn_from_and_acq_inner(in_reply_to);
if (target_thread == NULL) {
return_error = BR_DEAD_REPLY;
return_error_line = __LINE__;
goto err_dead_binder;
}
if (target_thread->transaction_stack != in_reply_to) {
//错误处理...
}
target_proc = target_thread->proc;
target_proc->tmp_ref++;
binder_inner_proc_unlock(target_thread->proc);
}
从transaction_stack中找到了reply的目标线程。之后就是创建binder_transaction t并对其赋值,复制数据等,和源码分析四中分析的代码大同小异。再然后就进入了for循环处理obj,而我们传入的是BINDER_TYPE_HANDLE类型的obj,处理代码如下:
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
struct flat_binder_object *fp;
fp = to_flat_binder_object(hdr);
ret = binder_translate_handle(fp, t, thread);
if (ret < 0) {
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
goto err_translate_failed;
}
} break;
读取obj后交给binder_translate_handle处理。
binder_translate_handle实现服务的传递
static int binder_translate_handle(struct flat_binder_object *fp,
struct binder_transaction *t,
struct binder_thread *thread)
{
struct binder_proc *proc = thread->proc;//即sm的binder_proc
struct binder_proc *target_proc = t->to_proc;//即客户端的binder_proc
struct binder_node *node;
struct binder_ref_data src_rdata;
int ret = 0;
//根据handle从sm的binder_proc中得到binder_ref,再返回binder_ref->node
node = binder_get_node_from_ref(proc, fp->handle,
fp->hdr.type == BINDER_TYPE_HANDLE, &src_rdata);
if (!node) {
binder_user_error("%d:%d got transaction with invalid handle, %d\n",
proc->pid, thread->pid, fp->handle);
return -EINVAL;
}
if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
ret = -EPERM;
goto done;
}
binder_node_lock(node);
//如果目标进程和node进程(服务进程)一致,则进入该if。
//不会进入该if,跳过
if (node->proc == target_proc) {
if (fp->hdr.type == BINDER_TYPE_HANDLE)
fp->hdr.type = BINDER_TYPE_BINDER;
else
fp->hdr.type = BINDER_TYPE_WEAK_BINDER;
fp->binder = node->ptr;
fp->cookie = node->cookie;
if (node->proc)
binder_inner_proc_lock(node->proc);
binder_inc_node_nilocked(node,
fp->hdr.type == BINDER_TYPE_BINDER,
0, NULL);
if (node->proc)
binder_inner_proc_unlock(node->proc);
trace_binder_transaction_ref_to_node(t, node, &src_rdata);
binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d -> node %d u%016llx\n",
src_rdata.debug_id, src_rdata.desc, node->debug_id,
(u64)node->ptr);
binder_node_unlock(node);
} else {//进入该else
struct binder_ref_data dest_rdata;
binder_node_unlock(node);
//源码分析四已经分析过该函数。如果忘了建议回看,这个函数非常重要。
ret = binder_inc_ref_for_node(target_proc, node,
fp->hdr.type == BINDER_TYPE_HANDLE,
NULL, &dest_rdata);
if (ret)
goto done;
fp->binder = 0;
fp->handle = dest_rdata.desc;//更新handle值为desc
fp->cookie = 0;
trace_binder_transaction_ref_to_ref(t, node, &src_rdata,
&dest_rdata);
binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d -> ref %d desc %d (node %d)\n",
src_rdata.debug_id, src_rdata.desc,
dest_rdata.debug_id, dest_rdata.desc,
node->debug_id);
}
done:
binder_put_node(node);
return ret;
}
又见到了binder_inc_ref_for_node,源码分析四中已经分析过它了,这里它的作用就是在客户端binder_proc中创建一个binder_ref指向服务的binder_node,并找一个空闲值(desc)作为该binder_ref在红黑树中的键值。
而后desc又赋给了obj的handle成员,这样当客户端得到obj时,它就得到了binder_ref的键值,也就可以取得binder_node,进而和服务端通信。当客户端用obj的handle值构造BpBinder后,就取得了服务端,这样就实现了服务从sm到客户端的传递。
binder_transaction分段2
if (reply) {
//tcomple放进本线程todo队列
binder_enqueue_thread_work(thread, tcomplete);
binder_inner_proc_lock(target_proc);
if (target_thread->is_dead) {
binder_inner_proc_unlock(target_proc);
goto err_dead_proc_or_thread;
}
BUG_ON(t->buffer->async_transaction != 0);
//pop目标线程的transaction_stack
binder_pop_transaction_ilocked(target_thread, in_reply_to);
//把t->work放进目标线程todo队列
binder_enqueue_thread_work_ilocked(target_thread, &t->work);
binder_inner_proc_unlock(target_proc);
//唤醒目标线程
wake_up_interruptible_sync(&target_thread->wait);
binder_restore_priority(current, in_reply_to->saved_priority);
binder_free_transaction(in_reply_to);
很简单,就是对todo队列和transaction_stack进行更新。
总结
总结一下,服务传递给客户端的过程其实就是在客户端创建引用的过程。创建过程分为两步,第一步是要得到服务的binder_node,该binder_node可以通过sm得到,因为服务向sm注册的时候会在sm留下一个binder_ref引用。第二步是创建一个指向binder_node的binder_ref引用和键值,放进客户binder_proc的红黑树中。键值就是handle值。
上述过程决定了不同进程对同一服务的handle值是不同的。handle值说白了就是binder_ref在红黑树中的键值。每个进程都有不同的红黑树,它们的键值肯定不相同。
到这里,Binder底层实现的主要代码基本上都过完了,下一篇文章将做最后的扫尾工作,介绍BBinder。