Android笔记 - Binder之处理注册Service组件请求
Posted demonyan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android笔记 - Binder之处理注册Service组件请求相关的知识,希望对你有一定的参考价值。
上篇文章以 MediaPlayerService 为例,分析了 Service 通过 Binder 驱动发送注册请求给 servicemanager 的过程。本文在此基础上分析 servicemanager 如何处理注册请求,以及如何反馈处理结果给 MediaPlayerService。
1. servicemanager 被唤醒
在上一篇文章 Binder之请求注册Service组件 中,分析到 binder_transaction 函数会创建一个待处理事务 t(事务类型是 BINDER_WORK_TRANSACTION),并将其添加到 servicemanager 进程的待处理工作队列 target_list 中,然后唤醒睡眠中的 servicemanager 进程,如下所示:
代码路径:linux/drivers/staging/android/binder.c
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply)
......
list_add_tail(&t->work.entry, target_list);
....
if (target_wait)
wake_up_interruptible(target_wait);
......
在 Binder之守护进程servicemanager 这篇文章中,讲到 servicemanager 进程通过 ioctl 系统调用检查 Binder 驱动是否有进程间通信请求需要它来处理。如果没有请求需要处理,那么 servicemanager 进程会在 binder_thread_read 函数中调用 wait_event_freezable_exclusive 进入睡眠等待状态。
代码路径:linux/drivers/staging/android/binder.c
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
void __user *buffer, int size,
signed long *consumed, int non_block)
....
if (wait_for_proc_work)
......
if (non_block)
if (!binder_has_proc_work(proc, thread))
ret = -EAGAIN;
else
ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
else
......
当 servicemanager 进程被唤醒时,会通过 binder_has_proc_work 函数来检查是否有新的请求需要处理,也就是检查当前进程的待处理工作队列 todo 是否为空,如下所示:
代码路径:linux/drivers/staging/android/binder.c
static int binder_has_proc_work(struct binder_proc *proc,
struct binder_thread *thread)
return !list_empty(&proc->todo) ||
(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
如果检测到待处理工作队列 todo 不为空,因此退出睡眠状态,接下来处理待处理工作队列中的进程间通信请求。
2. Binder 驱动处理待处理事务 - servicemanager 内核空间
servicemanager 退出睡眠后,继续执行 binder_thread_read 函数,将待处理事务从 Binder 驱动转发到 servicemanager 用户空间。过程如下所示:
代码路径:linux/drivers/staging/android/binder.c
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)
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)
w = list_first_entry(&proc->todo, struct binder_work, entry); [1]
else
if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
goto retry;
break;
if (end - ptr < sizeof(tr) + 4)
break;
switch (w->type)
case BINDER_WORK_TRANSACTION:
t = container_of(w, struct binder_transaction, work); [2]
break;
......
if (!t)
continue;
BUG_ON(t->buffer == NULL);
if (t->buffer->target_node)
struct binder_node *target_node = t->buffer->target_node;
tr.target.ptr = target_node->ptr; [3]
tr.cookie = target_node->cookie; [4]
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;
else
......
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, [5]
current->nsproxy->pid_ns);
else
tr.sender_pid = 0;
tr.data_size = t->buffer->data_size;
tr.offsets_size = t->buffer->offsets_size;
tr.data.ptr.buffer = (void *)t->buffer->data +
proc->user_buffer_offset; [6]
tr.data.ptr.offsets = tr.data.ptr.buffer +
ALIGN(t->buffer->data_size, sizeof(void *)); [7]
if (put_user(cmd, (uint32_t __user *)ptr)) [8]
return -EFAULT;
ptr += sizeof(uint32_t);
if (copy_to_user(ptr, &tr, sizeof(tr))) [9]
return -EFAULT;
ptr += sizeof(tr);
......
list_del(&t->work.entry);
t->buffer->allow_user_free = 1;
if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY))
t->to_parent = thread->transaction_stack;
t->to_thread = thread;
thread->transaction_stack = t; [10]
else
......
break;
......
return 0;
整个过程实际上是初始化描述事务数据的结构体 binder_transaction_data,并通过 copy_to_user 函数将其传递给 servicemanager 用户空间。
[1] 从待处理工作队列 todo 中取出一个工作项 w。
[2] 由于工作项类型为 BINDER_WORK_TRANSACTION,获取待处理事务 t 作为当前正在处理的事务。
[3] 由于 target_node 为 binder_context_mgr_node,所以 target_node->ptr 值为 NULL。
[4] 由于 target_node 为 binder_context_mgr_node,所以 target_node->cookie 值为 NULL。
[5] 将事务发起方的 pid 保存在 tr.sender_pid
中。
[6] 将通信数据 buffer 在内核空间的地址加上一个固定差值 proc->user_buffer_offset,得到通信数据 buffer 在 servicemanager 用户空间的地址。
[7] 通过通信数据 buffer 在 servicemanager 用户空间的地址,得到偏移数组在 servicemanager 用户空间的地址。
拷贝有深拷贝和浅拷贝之分:深拷贝需要重新分配内存资源,并且将内容完整的拷贝到分配好的内存空间;浅拷贝只是复制指向同一内存空间的地址,不需要分配新的内存资源。上述过程通过浅拷贝,实现了内存在内核空间和用户空间之间的共享,详细过程可以参考 Binder之守护进程servicemanager 中的
打开和映射 Binder 设备文件
小节。
[8] 调用 put_user 系统函数将协议 BR_TRANSACTION 拷贝回 servicemanager 用户空间。
[9] 调用 copy_to_user 系统函数将初始化好的结构体 binder_transaction_data 拷贝回 servicemanager 用户空间。
[10] 将当前处理事务 t 添加到 thread->transaction_stack 中,下面流程中还会用到这个事务。
3. servicemanager 处理注册请求 - servicemanager 用户空间
binder_thread_read 函数执行完返回到 binder_ioctl 函数,然后 ioctl 系统调用返回到 binder_loop 函数中,也就是从 Binder 驱动重新回到了 servicemanager 用户空间。接下来调用 binder_parse 函数解析从 Binder 驱动程序拷贝回来的 binder_transaction_data 结构体,将解析得到的 MediaPlayerService 名称以及句柄值保存到结构体 svcinfo,然后将结构体 svcinfo 添加到 svclist 链表中。详细过程请参考 Binder之守护进程servicemanager 中的 servicemanager 如何提供服务
小节。
当 Client 进程需要获取某个 Service 的代理对象时,servicemanager 根据服务名称遍历 svclist 链表,然后将对应的句柄值返回给 Client 进程,最后 Client 进程根据句柄值获得 Service 的代理对象。
servicemanager 处理完注册请求后,继续调用 binder_send_reply 函数返回处理结果。binder_send_reply 函数定义如下:
代码路径:frameworks/native/cmds/servicemanager/binder.c
void binder_send_reply(struct binder_state *bs,
struct binder_io *reply,
void *buffer_to_free,
int status)
struct
uint32_t cmd_free;
void *buffer;
uint32_t cmd_reply;
struct binder_txn txn;
__attribute__((packed)) data; [1]
data.cmd_free = BC_FREE_BUFFER; [2]
data.buffer = buffer_to_free;
data.cmd_reply = BC_REPLY; [3]
data.txn.target = 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.offs_size = 0;
data.txn.data = &status;
data.txn.offs = 0;
else
data.txn.flags = 0;
data.txn.data_size = reply->data - reply->data0;
data.txn.offs_size = ((char*) reply->offs) - ((char*) reply->offs0);
data.txn.data = reply->data0;
data.txn.offs = reply->offs0;
binder_write(bs, &data, sizeof(data)); [4]
[1] 定义结构体 data 用于保存返回给 Binder 驱动的内容。其中变量 cmd_free 用于保存释放内存的协议,指针 buffer 保存需要释放的内存地址。变量 cmd_reply 用于保存返回协议,结构体 binder_txn 用于保存请求处理返回结果。
[2] 释放内存协议为 BC_FREE_BUFFER。注册请求处理完后,需要释放之前 Binder 驱动在 binder_transaction 函数中分配的用于保存通信数据的内存。
[3] 返回协议为 BC_REPLY。
[4] 调用 binder_write 函数将结构体 data 传递给 Binder 驱动,binder_write 函数内部也是通过系统调用 ioctl(bs->fd, BINDER_WRITE_READ, &bwr)
来与 Binder 驱动进行交互。
4. Binder 驱动将注册结果返回给 Service - servicemanager 内核空间
上述 ioctl 系统调用最终也会执行到 Binder 驱动的 binder_ioctl 函数。通过之前几篇文章分析可知,binder_ioctl 中会调用 binder_thread_write 函数,后者又会调用 Binder 驱动的核心处理函数 binder_transaction,需要注意的是此时参数 reply 的值为 true。接下来看看这个过程:
代码路径:linux/drivers/staging/android/binder.c
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;
......
if (reply)
in_reply_to = thread->transaction_stack; [1]
......
thread->transaction_stack = in_reply_to->to_parent; [2]
target_thread = in_reply_to->from; [3]
......
target_proc = target_thread->proc; [4]
else
......
if (target_thread)
e->to_thread = target_thread->pid;
target_list = &target_thread->todo; [5]
target_wait = &target_thread->wait; [6]
else
target_list = &target_proc->todo;
target_wait = &target_proc->wait;
e->to_proc = target_proc->pid;
/* TODO: reuse incoming transaction for reply */
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);
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
......
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);
trace_binder_transaction(reply, t, target_node);
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;
t->buffer->target_node = target_node;
trace_binder_transaction_alloc_buf(t->buffer);
if (target_node)
binder_inc_node(target_node, 1, 0, NULL);
offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size))
......
if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size))
......
off_end = (void *)offp + tr->offsets_size;
for (; offp < off_end; offp++) [7]
......
if (reply)
BUG_ON(t->buffer->async_transaction != 0);
binder_pop_transaction(target_thread, in_reply_to); [8]
......
t->work.type = BINDER_WORK_TRANSACTION;
list_add_tail(&t->work.entry, target_list);
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
list_add_tail(&tcomplete->entry, &thread->todo);
if (target_wait)
wake_up_interruptible(target_wait);
return;
......
[1] 初始化 in_reply_to 为指向当前线程 thread(servicemanager 线程) 的事务堆栈 binder_transaction 的指针。前面 binder_thread_read 函数将处理事务加入到了 thread 的事务堆栈,因此 in_reply_to 指针实际指向了之前加入的事务。
[2] 将 in_reply_to 指向的事务从当前线程的事务堆栈中退栈。
[3] 根据 in_reply_to 初始化目标线程 target_thread 为 MediaPlayerService 线程。
[4] 根据 target_thread 初始化目标进程 target_proc 为 MediaPlayerService 进程。
[5] 初始化 target_list 为目标线程 target_thread 的待处理工作队列。
[6] 初始化 target_wait 为目标线程 target_thread 的等待队列。
[7] 由于返回结果数据中不存在 Binder 对象,因此不会进入 for 循环。
[8] 调用 binder_pop_transaction 函数将 in_reply_to 指向的事务从目标线程的事务堆栈中退栈。
这里省略了与文章 Binder之请求注册Service组件中分析binder_transaction 函数重复的内容。
和之前的分析一样,最后会将待处理事务 t 和待完成工作项 tcomplete 分别添加到 target_list 和 thread->todo
中,因此待处理事务 t 将由 MediaPlayerService 来处理,而待完成工作项 tcomplete 将由 servicemanager 来处理,处理过程之前也都分析过。
至此,Service 注册流程终于分析完成,下图为注册流程所涉及的 MediaPlayerService,Binder 驱动和 servicemanager 三者之间的交互过程:
参考文献:
1. Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析
2. Android Binder机制(六) addService详解之请求的处理
以上是关于Android笔记 - Binder之处理注册Service组件请求的主要内容,如果未能解决你的问题,请参考以下文章
Android笔记 - Binder之请求注册Service组件
Android笔记 - Binder之Client请求Service代理对象
Android笔记 - Binder之Client请求Service代理对象
Android笔记 - Binder之守护进程servicemanager