Android框架层提供了本地系统服务和java系统服务,那么这些服务是如何检索的呢,下面简要分析一下这个过程。
一、服务检索
服务检索是指调用服务的客户端向Context Manager(service manager中第一个注册的节点)请求指定服务的handle的过程。调用服务的客户端把包含服务请求信息的IPC数据发送给Context Manager,然后Context Manager通过IPC应答数据将指定服务的handle发送给它。Binder Driver将把调用服务的客户端请求的服务的binder_node结构体注册到客户端的bind_proc结构体中。
针对上面这段话有三点需要解释一下:
1、service_manager如何查找指定服务的handle,如下代码:
struct svcinfo { struct svcinfo *next; uint32_t handle; struct binder_death death; int allow_isolated; size_t len; uint16_t name[0]; }; struct svcinfo *svclist = NULL; struct svcinfo *find_svc(const uint16_t *s16, size_t len) { struct svcinfo *si; for (si = svclist; si; si = si->next) { if ((len == si->len) && !memcmp(s16, si->name, len * sizeof(uint16_t))) { return si; } } return NULL; }上面就是service_manager.c中的检索服务代码,可以看出service_manager维护一张服务表svclist,可以根据服务名查找对应的结构体svcinfo,这个结构体里面就包含了需要的handle变量,handle值会被保存在BpBinder里面的mHandle变量里面。
2、binder_node是什么?下面是一段服务注册过程中的代码:
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); fp->handle = ref->desc; }注意:Service Manager里面保存的handle值就是这边binder_ref里面的desc属性
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; };所以binder_node就是服务注册时传入的服务对象,其中第一个参数proc是代表Service Server的binder_proc,因为所有的服务实体都是注册到Service Server,而后面的binder_get_ref_for_node则是将该实体对应的引用注册到Context Manager,即Context Manager拥有所有服务实体对应的引用,而Service Server拥有所有实体,下面是binder_node结构体:
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; };其中proc是Service Server进程的binder proc地址,ptr是远程binder对象地址,即BpBinder,cookie是注册的本地服务的binder地址,每次Service Server接收到消息会先把cookie转换成对应的Service对象,然后根据传入的方法编号选择对应的方法执行生成结果数据,这样Service Server只要负责执行就行了,不用管服务对象的查找。
3、什么是binder_proc?首先看一下binder_open函数:
static int binder_open(struct inode *nodp, struct file *filp){ struct binder_proc *proc; proc = kzalloc(sizeof(*proc), GFP_KERNEL); if(proc == NULL) return -ENOMEM; get_task_struct(current); proc->tsk = current; INIT_LIST_HEAD(&proc->todo); init_waitqueue_head(&proc->wait); proc->default_priority = task_nice(current); mutex_lock(&binder_lock); binder_stats.obj_created[BINDER_STAT_PROC]++; hlist_add_head(&proc->proc_node, &binder_procs); proc->pid = current->group_leader->pid; INIT_LIST_HEAD(&proc->delivered_death); filp->private_data = proc; mutex_unlock(&binder_lock); if(binder_proc_dir_entry_proc){ snprintf(strbufm sizeof(strbuf), "%u", proc->pid); create_proc_read_entry(strbuf,S_IRUGO,binder_proc_dir_entry_proc, binder_read_proc_proc, proc); return 0; } ... } struct binder_proc{ struct rb_root threads,//拥有binder_thread结构体的红黑树的根 struct rb_root nodes,//带有binder_node结构体的红黑树的根 struct rb_root refs_by_desc,//带有binder_ref结构体的红黑树的根,使用desc区分各个binder_ref结构体 int pid,//创建binder_proc结构体的进程id struct vm_area_struct *vms,//调用binder_mmap()函数的进程的用户空间信息(用来保证内核空间内存) struct task_struct *tsk,//指生成binder_proc结构体的进程的task_struct结构体 void *buffer,//接收IPC数据binder_buffer结构体指针 size_t user_buffer_offset,//映射为接收IPC数据的buffer的内核空间与用户空间的地址偏移量,用来将接收到IPC数据传递到用户空间 struct list_head buffers,//为接收IPC数据而分配的binder_buffer结构体列表 struct rb_root free_buffers,//接收IPC数据后要释放的binder_buffer结构体列表 size_t buffer_size,//进程在内核空间开辟的buffer大小 struct list_head todo,//进程从待机状态唤醒后要做的事情 wait_queue_head_t wait//用来让进程进入待机状态 }从上面函数可以看出每个进程调用binder_open都会生成自己的binder_proc,从binder_proc结构体可以看出进程间通信主要是靠每个进程在内核空间开辟的一块buffer,通信时一方会把自己的数据传到自己开辟的Buffer里面,然后binder driver负责将这些数据拷贝到目标进程的buffer里,然后唤醒目标进程处理数据就行了。
服务检索过程
1) Context Manager进入待机状态,等待接收IPC数据。
2) 调用服务的客户端生成并初始化binder_proc结构体,而后开辟一块buffer用于接收IPC应答数据
3) 调用服务的客户端通过ioctl()调用binder driver的binder_ioctl()函数。与注册服务时一样,binder driver会查找Context Manager的binder_proc结构体。但与注册服务不同的是,它并不生成binder_node结构体,只是将IPC数据拷贝到Context Manager的接收buffer中。并且记下调用服务客户端的binder_proc结构体,以便查找IPC应答数据的接收端。
4) Binder driver让调用服务的客户端处于待机状态,并唤醒处于待机状态的Context Manager,而后接收IPC数据。从待机状态苏醒的Context Manager在服务目录中查找请求的服务,把服务编号插入到IPC应答数据中,并把IPC应答数据传递给binder driver.
5) Binder dirver将(4)中接收到服务编号查找相应的binder_node结构体(先查找对应结构体的引用,引用里的一个属性就是binder_node),而后将查找到binder_node结构体注册到调用服务的客户端的binder_proc中。binder_node结构体用于在调用服务时查找Service Server的binder_proc结构体.
6) Binder driver将(5)中注册的binder_node结构体的编号插入到IPC应答数据中,传递给客户端,然后唤醒客户端。在使用服务时,客户端将编号作为handle使用。
二、服务使用
经过服务检索,客户端获得了Service Server所拥有服务的binder_node,在生成IPC数据时,它就可以把要使用服务的binder节点编号设置到IPC数据的handle中,取代原来值为0的handle。
服务使用过程
7) Service Server处于待机状态,等待接收IPC数据。
8) 客户端将从服务检索中获取的binder节点编号保存到IPC数据的handle(等于binder_ref中的desc)中,生成IPC数据后传递给binder driver,binder driver根据IPC数据中的handle查找相应的binder_ref结构体(ContextManager的binder proc结构体里面保存了所有的binder_ref对象,可以根据desc属性查找对应的binder_ref对象),然后获取binder_ref里面的binder_node对象,而binder_node结构体里面的proc属性就是注册该服务的Service Server的binder_proc地址。最后通过binder_proc结构体中binder_buffer结构体,将IPC数据拷贝到Service Server进程的buffer中。
9) Binder driver让客户端进入待机状态,唤醒处于待机状态的Service Server。Service Server从待机状态苏醒后接受IPC数据,并通过IPC数据中的RPC代码、RPC数据调用相应的服务函数。
10) 在服务函数执行完毕后,Service Server会生成IPC应答数据,并将其传递给binder driver,binder driver执行的动作与服务注册阶段相同,将IPC数据传递给客户端。至此,客户端调用服务的整个IPC过程就完成了。
三、其他
android context.getSystemService方法获取的是注册在SystemServiceRegistry里面的服务,里面的服务有的是来自系统服务,有的不是,比如Context.WINDOW_SERVICE就不是系统的,而是return new WindowManagerImpl(ctx);