android binder机制之--(我是Service Manager)

Service Manager 上篇文章android binder机制之--(我是binder)介绍了binder机制的概念,特点,应用模式和框架组成,这篇文章我们来介绍一下Android系统Binder机制的服务总管--Service Manager,service Manager在android binder机制中的低位那是相当重要了,所有的Server(System Server)都需要向他注册,应用程序需要向其查询相应的服务。

Service Manager这么厉害,那也不是谁都能成为这位大管家的。要想成为Service Manager,那自然要有两把刷子,下面就来分析这位服务管家是如何诞生的。我这里没有画出流程图,所以只能以代码展示出来了,因为每个文件都有很多代码,所以我只贴出重要的部分,说到哪里,就贴出哪里的代码,(你也可以参考源码来分析)这样会更容易理解所说的内容。在Android系统中,Service Manager的源码位于:

frameworks\base\cmds\servicemanager\service_manager.c

int main(int argc, char **argv)

{

struct binder_state *bs;/*定义一个binder驱动结构表示驱动状态的一个数据结构,里面记录了打开驱动的句柄即文件描述符,分配的内存空间以及内存空间的大小。*/

void *svcmgr = BINDER_SERVICE_MANAGER; /*服务管理进程的句柄被定义为0,如下所示:#define BINDER_SERVICE_MANAGER ((void*) 0)*/

bs = binder_open(128*1024);

if (binder_become_context_manager(bs)) {

LOGE("cannot become context manager (%s)\n", strerror(errno));

return -1;

}

svcmgr_handle = svcmgr;

binder_loop(bs, svcmgr_handler);

return 0;

}

没错,你看到了,这就是传说中的main函数,这说明ServiceManager就是一个进程,如果你不相信,在android的启动脚本init.rc里,我们可以找到答案:

service servicemanager /system/bin/servicemanager #一个系统服务服务

user system #用户

critical

onrestart restart zygote

onrestart restart media

上面的启动代码说明ServiceManager是Android的核心程序,可执行文件就是/system/bin/servicemanager,开机后就会自动运行。main函数是一个进程的入口,下面就让我从进程的入口出分析这段代码吧!我们以函数的调用流程为主线,来介绍Service Manager,会列出主要数据结构的定义。

(1)binder_open()

我们看到它先调用binder_open()函数,这个函数的主要功能:打开binder设备(/dev/binder),然后将该文件映射到内存中,并返回这块内存的首地址。这样我们就可以像操作内存一样,来操作这个文件了。

struct binder_state *binder_open(unsigned mapsize)

{

struct binder_state *bs;

bs = malloc(sizeof(*bs)); /*向系统申请分配指定size个字节的内存空间。返回类型是 void* 类型。void* 表示未确定类型的指针。void* 类型可以强制转换为任何其它类型的指针。*/

if (!bs) {

errno = ENOMEM;

return 0;

}

bs->fd = open("/dev/binder", O_RDWR);/*打开/dev/binder设备节点,返回一个文件描述符,这个描述符很重要,在后面的通讯中会频繁用到*/

if (bs->fd < 0) {

fprintf(stderr,"binder: cannot open device (%s)\n",

strerror(errno));

goto fail_open;

}

bs->mapsize = mapsize;

bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);

/*将一个文件或者其它对象映射进内存,NULL表示映射区的开始地址;mapsize 表示映射区的长度;PROT_READ为期望的内存保护标志,表示页内容可以被读取;MAP_PRIVATE 为指定映射对象的类型;bs->fd表示有效的文件描述符;0表示被映射对象内容的起点。*/

if (bs->mapped == MAP_FAILED) {

fprintf(stderr,"binder: cannot map device (%s)\n",

strerror(errno));

goto fail_map;

}

/* TODO: check version */

return bs;

fail_map:

close(bs->fd);// 关闭文件句柄关闭文件描述符

fail_open:

free(bs);//释放内存

return 0;

}

binder_state是表示驱动状态的一个数据结构,里面记录了打开驱动的句柄即文件描述符,分配的内存空间以及内存空间的大小。我们看看它的定义:

frameworks\base\cmds\servicemanager\binder.c

struct binder_state

{

int fd; //设备文件描述符

void *mapped; //文件映射的内存地址

unsigned mapsize; //文件映射内存的大小

};

(2)binder_become_context_manager()

就是这个函数使他自己变为了android binder机制的服务管家,其代码如下:

frameworks\base\cmds\servicemanager\binder.c

int binder_become_context_manager(struct binder_state *bs)

{

return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);

/*发送设置服务管家的命令到binder驱动,binder驱动做相应的处理,使其成为服务大管家,这里对binder的驱动部分不做详细介绍了*/

}

binder.c文件也是framework框架的内容,它是与binder驱动交互的接口。

这个函数通知binder kernel驱动程序这个进程将作为SystemService Manager,使它自己变为了Server管理者,告诉Binder Kernel驱动程序这是一个服务管理进程。ioctl函数对BINDER_SET_CONTEXT_MGR的具体底层操作不做详细介绍,我们只要知道驱动为我们做了什么事就好:设置驱动中的全局变量binder_context_mgr_uid为当前进程的uid,并初始化一个binder_node赋值给全局变量binder_context_mgr_node。完成了注册Service Manager的工作。

(3)binder_loop()

从上一篇文章可以看出Service Manager是一个系统守护进程,作为一个Server大总管,本身也是一个server,它管理着系统的各个服务。

既然是一个server就要时刻准备为客户端提供服务,可不能忘了自己的责任那!它负责监听是否有其他程序向其发送请求,如果有请求就响应。每个服务都要在ServiceManager中注册,而请求服务的客户端去ServiceManager请求服务。要是Service Manager能调用一个循环函数进入到循环状态,再提供一个回调处理函数,用于处理不同的请求那就好了!

没错,这个服务大管家就是这么干的,Binder_loop()就是这个守护进程的核心—循环体,而svcmgr_handler()就是这个回调函数。

这里多说一句,有木有想过服务端和客户端是怎么与Service Manager通讯,请求服务的呢?实际上它们需要在自己进程中创建一个服务代理,才能与服务管家通讯,那么客户端(对于Serivce Manger来说,我们所说的)怎样它的才能怎样生成他的服务代理对象呢?答案是binder设备(/devbinder)为每一个服务维护一个句柄,调用binder_become_context_manager函数变为“Server大总管”的服务,他的句柄永远是0,是一个“众所周知”的句柄,这样每个程序都可以通过binder机制在自己的进程空间中创建一个Service Manager代理对象了。其他的服务在binder设备在设备中的句柄是不定的,需要向“Server大总管”查询才能知道。

void binder_loop(struct binder_state *bs, binder_handler func)

{

int res;

struct binder_write_read bwr; //一个读写数据结构

unsigned readbuf[32];

bwr.write_size = 0;

bwr.write_consumed = 0;

bwr.write_buffer = 0;

readbuf[0] = BC_ENTER_LOOPER; //控制命令

binder_write(bs, readbuf, sizeof(unsigned));

for (;;) {

bwr.read_size = sizeof(readbuf);

bwr.read_consumed = 0;

bwr.read_buffer = (unsigned) readbuf;

//通过设备描述符,将数据发给binder驱动

res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

if (res < 0) {

LOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));

break;

}

//解析驱动发来的数据,调用回调函数func

res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);

if (res == 0) {

LOGE("binder_loop: unexpected reply?!\n");

break;

}

if (res < 0) {

LOGE("binder_loop: io error %d %s\n", res, strerror(errno));

break;

}

}

}

Binder_loop()中传递了一个回调函数,这个回调函数在binder_parse()函数中被调用,用来处理驱动发来的消息,消息的解析这里就不介绍了,我们重点看看这个回调函数都干了什么?

int svcmgr_handler(struct binder_state *bs,

struct binder_txn *txn,

struct binder_io *msg,

struct binder_io *reply)

{

……

……

switch(txn->code) {

caseSVC_MGR_GET_SERVICE://获取服务的请求,来自客户端

caseSVC_MGR_CHECK_SERVICE://查找服务的请求,来自客户端

s = bio_get_string16(msg, &len);

ptr = do_find_service(bs, s, len);//查找服务

if (!ptr)

break;

bio_put_ref(reply, ptr);

return 0;

caseSVC_MGR_ADD_SERVICE://添加系统服务的请求,自然是来自系统服务

s = bio_get_string16(msg, &len);

ptr = bio_get_ref(msg);

if (do_add_service(bs, s, len, ptr, txn->sender_euid))

return -1;

break;

case SVC_MGR_LIST_SERVICES: {

unsigned n = bio_get_uint32(msg);

si = svclist;

……

……

return 0;

}

从上面的代码很容易看出,守护进程(服务管家)循环从binder设备文件读取数据,然后解析并响应请求,包括服务端的添加服务请求和客户端的查询,获取服务的请求。现在有两个主要的调用分支,应该先说那个?是先有蛋还是先有鸡?咱不去讨论,那是现有服务端,还是现有客户端呢?一般来说,系统会先启动服务,服务向管家请求注册服务,然后才有客户端的服务请求。不过,还是有服务端没了,客户端还是存在的情况,最多查询不到呗!咱也不必纠结,还是按照一般思路来讲解吧!

1)do_add_service()

int do_add_service(struct binder_state *bs,

uint16_t *s, unsigned len,

void *ptr, unsigned uid)

{

struct svcinfo *si;

if (!ptr || (len == 0) || (len > 127))

return -1;

if (!svc_can_register(uid, s)) { //查看该服务是否有注册权限

LOGE("add_service('%s',%p) uid=%d - PERMISSION DENIED\n",

str8(s), ptr, uid);

return -1;

}

si = find_svc(s, len); //在服务列表中查找服务,查看该服务是否已经注册

if (si) {

if (si->ptr) {

LOGE("add_service('%s',%p) uid=%d - ALREADY REGISTERED\n",

str8(s), ptr, uid);

return -1; //如果已经注册,拒绝添加

}

si->ptr = ptr;

} else {

si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));

if (!si) {

LOGE("add_service('%s',%p) uid=%d - OUT OF MEMORY\n",

str8(s), ptr, uid);

return -1;

}

si->ptr = ptr;

si->len = len;

memcpy(si->name, s, (len + 1) * sizeof(uint16_t));

si->name[len] = '\0';

si->death.func = svcinfo_death;

si->death.ptr = si;

si->next = svclist;/*svclist就是服务管家维护的服务列表,它是一个全局变量,这里完成服务在服务链表中添加的操作*/

svclist = si;

}

binder_acquire(bs, ptr);

binder_link_to_death(bs, ptr, &si->death);

return 0;

}

我们看到这个函数,首先检查是否有权限注册service,没权限就对不起了,出错返回;然后检查是否已经注册过,注册过的service将不能再次注册。然后构造一个svcinfo对象,并加入一个全局链表中svclist中。最后通知binder设备:有一个service注册进来。

2)do_find_services()

void *do_find_service(struct binder_state *bs, uint16_t *s, unsigned len)

{

struct svcinfo *si;

si = find_svc(s, len);

// LOGI("check_service('%s') ptr = %p\n", str8(s), si ? si->ptr : 0);

if (si && si->ptr) {

return si->ptr;

} else {

return 0;

}

}

Do_find_services()函数中调用了find_svc(),find_svc()函数定义如下:

struct svcinfo *find_svc(uint16_t *s16, unsigned 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 0;

}

在Service Manager维护的服务链表中,查找指定服务的名字和大小,如果找到,返回对应的svcinfo结构的一个指针,否则,返回空。

本文只是简单分析了一下Service Manager,更多的细节没有设计,只是从整体框架进行了简略的分析,如果要详细了解,请参考源码。在后面,我们会继续分析Android binder机制之--(我是系统服务server)。

更多相关文章

  1. Android开发者的Ane简单入门
  2. Android构建时报错: java.io.IOException: Could not parse XML f
  3. android的布局文件简介
  4. Android(安卓)图形系统SurfaceFlinger
  5. Android(安卓)Studio 导出 .aar包的操作流程
  6. Android(安卓)SVG动画PathView源码解析与使用教程(API 14)
  7. Android(java)学习笔记106:Android(安卓)XML解析
  8. Android(安卓)JNI环境搭建及开发入门
  9. Android(安卓)数据存储之文件存储小记

随机推荐

  1. android上传文件
  2. android 启动延迟加载画面
  3. Android 创建单独的服务运行在后台(无界面
  4. ContentProvider与ContentResolver实现数
  5. Android中分享功能的使用
  6. android中TextView嵌套在ScrollView中并
  7. Android BroadcasetReceiver
  8. Android大图片加载处理
  9. Android重写返回按钮退回桌面(根据源码查
  10. Using Android's Compatibility Test Sui