前言
众所皆知,Android系统是当前占据用户量最大的手机系统,国内流行的客户端开发无非是android 与 ios,即使作为一个普通用户,小伙伴们可曾想过这个给我们生活带来巨大变化的系统是如何为我们服务的呢?
Android系统可大致分为五层,如下图,第一层就是我们平时看到的各种应用了,这个小伙伴们肯定熟悉,这里就不多说了,要给大家介绍的是第二层——Framework,它直接对接应用程序,提供了本地系统服务和java系统服务。
那么这些服务是如何使用的呢,下面简要分析一下这个过程
Android Framework
先来看一个应用的启动过程
如上图,一个应用启动主要依赖于ActivityManager服务,它是系统提供的众多服务里的其中一个,服务作为framework提供的主要内容,扮演着承上启下的重要责任:一方面为应用提供约定好的功能调用接口,另一方面无需应用关心服务如何实现,framework会负责与底下驱动层进行交互,达到目的。
正因为有了framework层,应用开发才能事半功倍,专注于业务逻辑实现,接下来就来介绍一下服务如何运行.
1、服务检索
服务检索是指调用服务的客户端向Context Manager(service manager中第一个注册的节点)请求指定服务的handle的过程。调用服务的客户端把包含服务请求信息的IPC数据发送给Context Manager,然后Context Manager通过IPC应答数据将指定服务的handle发送给它。Binder Driver将把调用服务的客户端请求的服务的binder_node结构体注册到客户端的bind_proc结构体中。
针对上面这段话有三点需要解释一下:
1)service_manager如何查找指定服务的handle,如下代码:
struct svcinfo
上面就是service_manager.c中的检索服务代码,可以看出service_manager维护一张服务表svclist,可以根据服务名查找对应的结构体svcinfo,这个结构体里面就包含了需要的handle变量,handle值会被保存在BpBinder里面的mHandle变量里面。
2)binder_node是什么?下面是一段服务注册过程中的代码:
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
来看一下bind_ref结构体:
struct binder_ref {
所以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 {
其中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){
从上面函数可以看出每个进程调用binder_open都会生成自己的binder_proc,从binder_proc结构体可以看出进程间通信主要是靠每个进程在内核空间开辟的一块buffer,通信时一方会把自己的数据传到自己开辟的Buffer里面,然后binder driver负责将这些数据拷贝到目标进程的buffer里,然后唤醒目标进程处理数据就行了。
总结起来服务检索过程有如下几步:
-
Context Manager(ServiceManager)进入待机状态,等待接收IPC数据。
-
调用服务的客户端生成并初始化binder_proc结构体,而后开辟一块buffer用于接收IPC应答数据
-
调用服务的客户端通过ioctl()调用binder driver的binder_ioctl()函数。与注册服务时一样,binder driver会查找Context Manager的binder_proc结构体。但与注册服务不同的是,它并不生成binder_node结构体,只是将IPC数据拷贝到Context Manager的接收buffer中。并且记下调用服务客户端的binder_proc结构体,以便查找IPC应答数据的接收端。
-
Binder driver让调用服务的客户端处于待机状态,并唤醒处于待机状态的Context Manager,而后接收IPC数据。从待机状态苏醒的Context Manager在服务目录中查找请求的服务,把服务编号插入到IPC应答数据中,并把IPC应答数据传递给binder driver.
-
Binder dirver将(4)中接收到服务编号去Context Manager中查找相应的binder_node结构体(先查找对应结构体的引用,引用里的一个属性就是binder_node),而后将查找到binder_node结构体注册到调用服务的客户端的binder_proc中。binder_node结构体用于在调用服务时查找Service Server的binder_proc结构体.
-
Binder driver将(5)中注册的binder_node结构体的编号插入到IPC应答数据中,传递给客户端,然后唤醒客户端。在使用服务时,客户端将编号作为handle使用。
2、服务使用
经过服务检索,客户端获得了Service Server所拥有服务的binder_node,在生成IPC数据时,它就可以把要使用服务的binder节点编号设置到IPC数据的handle中,取代原来值为0的handle,服务使用有如下过程:
-
Service Server处于待机状态,等待接收IPC数据。
-
客户端将从服务检索中获取的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中。
-
Binder driver让客户端进入待机状态,唤醒处于待机状态的Service Server。Service Server从待机状态苏醒后接受IPC数据,并通过IPC数据中的RPC代码、RPC数据调用相应的服务函数。
-
在服务函数执行完毕后,Service Server会生成IPC应答数据,并将其传递给binder driver,binder driver将IPC数据传递给客户端。至此,客户端调用服务的整个IPC过程就完成了。