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

时间:2021-12-02 05:28:17
 

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)。