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驱动程序这个进程将作为System Service 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) {
case SVC_MGR_GET_SERVICE://获取服务的请求,来自客户端
case SVC_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;
case SVC_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)。