Android笔记 - Binder之处理注册Service组件请求
来源:互联网 发布:mac更换硬盘克隆系统 编辑:程序博客网 时间:2024/06/01 10:25
上篇文章以 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.cstatic 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.cstatic 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.cstatic 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.cstatic 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.cvoid 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.cstatic 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 service 之 Binder
- Android - Binder机制 - 普通service注册
- Android学习笔记--四大组件之Service
- Android四大组件之Service(笔记)
- Android四大组件之Service(笔记)续
- #Android笔记#四大组件之Service
- 学习笔记:Android基本组件之Service
- Android四大组件之Service复习笔记
- Android学习笔记四大组件之Service
- Binder进程间通信机制的Service Manager组件响应Service组件的注册过程
- Android笔记----Service组件
- android笔记--处理started service的多次启动请求
- android笔记--处理started service的多次启动请求
- android组件之Service
- Android之Service组件
- 简单而又完整的Makefile-转
- 无限网络无限连接掉网的解决
- Sublime Text3常用的快捷键
- Windows下C++多线程同步与互斥简单运用-转
- Fragment的onResume
- Android笔记 - Binder之处理注册Service组件请求
- 16道嵌入式C语言面试题
- 嵌入式题
- 数据挖掘算法【一】c4.5算法的理解
- c语言中qsort的使用方法
- Gerrit 手动安装
- iOS数据库SQL
- MySql 数据库分区
- iOS二维码的扫描 全屏显示局部扫描