Android进程间通信(IPC)Binder机制——Service Manager

来源:互联网 发布:mac系统如何安装sass 编辑:程序博客网 时间:2024/06/06 16:50

前言

    我觉得搞懂Android进程间通信机制也是很牛逼的事情,是比较牛逼的事情我都想去学习。学习了老罗关于Android进程间通信的系列博客,很难理解,涉及的知识点很多,只能一点点的学习。最近确实是有很多烦心的事情,有些无奈,但是我觉得自己遇到任何事情都应该积极去面对,不逃避。

概述

    ServiceManager是Binder进程间通信机制的核心组件之一,它扮演着Binder进程间通信机制上下文管理者(Context Manager)的角色,同时负责管理系统中的Service组件,并且向Client组件提供获取Service代理对象的服务。ServiceManager运行在一个独立的进程中,因此,Service组件和Client组件也需要通过进程间通信机制来和它交互,而采用的进程间通信机制正好也是Binder进程间通信机制。从这个角度来看,Service Manager除了是Binder进程间通信机制的上下文管理者(Context Manager)之外,它也是一个特殊的Service组件。
    ServiceManager是由init进程负责启动的,而init进程是在系统启动时启动的,因此ServiceManager也是在系统启动时启动的,它的启动脚本如下所示(来源为:华为荣誉3x的Rom包):
service servicemanager /system/bin/servicemanager    class core    user system    group system    critical    onrestart restart healthd    onrestart restart zygote    onrestart restart media    onrestart restart surfaceflinger    onrestart restart drm    onrestart restart aal    onrestart restart bwc
    第一行的关键字service表明ServiceManager是以服务的形式启动的,它对应的程序文件和进程名称分别为:/system/bin/servicemanager和servicemanager。第三行关键字user表明ServiceManager是以系统用户system的身份运行的。第四行的关键字critical表明ServiceManager是系统中一个关键服务。在系统的运行过程中,关键服务器是不可以退出的,一旦退出,就会被系统重启,但是如果一个关键服务在4分钟内退出的次数大于4,那么系统就会重启,然后进入恢复模式。剩下几行中的onrestart表示一旦ServiceManager被系统重启,也需要将zygote、media等几个进程一起重新启动。
    Service Manager的源代码位于“frameworks/native/cmds/servicemanager"目录下,主要由binder.h、binder.c和service_manager.c三个文件组成。Service Manager的入口位于service_manager.c文件的main函数:
int main(int argc, char **argv){    struct binder_state *bs;    void *svcmgr = BINDER_SERVICE_MANAGER;    bs = binder_open(128*1024);    if (binder_become_context_manager(bs)) {        ALOGE("cannot become context manager (%s)\n", strerror(errno));        return -1;    }    svcmgr_handle = svcmgr;    binder_loop(bs, svcmgr_handler);    return 0;}
    main函数主要有三个功能:
  1. 调用函数binder_open打开设备文件/dev/binder,并且将该文件通过mmap映射到本进程的地址空间。
  2. 调用函数binder_become_context_manager告诉Binder驱动程序自己是Binder上下文管理者。
  3. 调用函数binder_loop来循环等待和处理Client进程的通信请求。
    进入这三个功能之前,先来看一下这里用到的结构体binder_state、宏BINDER_SERVICE_MANAGER的定义。
    struct binder_state定义在“frameworks/native/cmds/servicemanager/binder.c”文件中:
struct binder_state{    int fd;    void *mapped;    unsigned mapsize;};
    fd是文件描述符,即表示打开的/dev/binder设备文件描述符;mapped是把设备文件/dev/binder映射到进程空间的起始地址;mapsize是上述映射空间的大小。
    宏BINDER_SERVICE_MANAGER定义在“frameworks/native/cmds/servicemanager/binder.h”文件中:
#define BINDER_SERVICE_MANAGER ((void*) 0)
    它表示Service Manager的句柄为0。Binder通信机制使用句柄来代表远程接口,这个句柄的意义和Windows编程用到的句柄是差不多的概念。前面说到,Service Manager在充当守护进程的同时,它还充当着Server的角色,当它作为远程接口使用时,它的句柄值便为0,这就是它的特殊之处,其余的Server的远程接口句柄值都是一个大于0而且由BInder驱动程序自动进行分配的值。

打开和映射Binder设备文件

    函数binder_open用了打开设备文件/dev/binder,并且将它映射到进程的地址空间,这个函数的实现位于“/frameworks/native/cmds/servicemanager/binder.c”文件中:
struct binder_state *binder_open(unsigned mapsize){    struct binder_state *bs;    bs = malloc(sizeof(*bs));    if (!bs) {        errno = ENOMEM;        return 0;    }    bs->fd = open("/dev/binder", O_RDWR);    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);    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_open的调用过程可以知道,参数mapsize的大小为128*1024,即128KB。在使用函数open打开设备文件/dev/binder的时候,Binder驱动程序中的函数binder_open就会被调用,它会为当前进程创建一个binder_proc结构体,用了描述当前进程的Binder进程间通信状态。
    调用函数mmap将设备文件/dev/binder映射到i进程的地址空间,它请求映射的地址空间大小为mapsize,即请求Binder驱动程序为进程分配128K大小的内核缓冲区。映射后得到的地址空间的起始地址和大小分别保存在一个binder_state结构体bs的成员变量mapped和mapsize中。最后,将binder_state结构体bs返回给调用者,即函数main。
    打开了设备文件/dev/binder,以及将它映射到进程的地址空间之后,Service Manager接下来就会将自己注册为Binder进程间通知机制的上下文管理者。

注册为Binder上下文管理者

    ServiceManager要成为Binder进程间通信机制的上下文管理者,就必须要通过IO控制命令BINDER_SET_CONTEXT_MGR将自己注册到Binder驱动程序中,这是通过调用函数binder_become_context_manager来实现的。函数位于"frameworks/base/cmds/servicemanager/binder.c"文件中:
int binder_become_context_manager(struct binder_state *bs){    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);}
    IO控制命令 BINDER_SET_CONTEXT_MGR的定义如下所示:
#defineBINDER_SET_CONTEXT_MGR_IOW('b', 7, int)
    它只有一个整型参数,用来描述一个与ServiceManager对应的Binder本地对象的地址值。由于与Service Manager对应的Binder本地对象是一个虚拟的对象,并且它的地址值等于0,因此,函数binder_become_context_manager就将IO控制命令BINDER_SET_CONTEXT_MGR的参数设置为0。Binder驱动程序是在它的函数binder_ioctl中处理IO控制命令BINDER_SET_CONTEXT_MGR的,代码位置(“kernel/drivers/staging/android/binder.c”),源码如下所示:
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){int ret;struct binder_proc *proc = filp->private_data;struct binder_thread *thread;unsigned int size = _IOC_SIZE(cmd);void __user *ubuf = (void __user *)arg;binder_lock(__func__);thread = binder_get_thread(proc);if (thread == NULL) {ret = -ENOMEM;goto err;}switch (cmd) {case BINDER_SET_CONTEXT_MGR:if (binder_context_mgr_node != NULL) {printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");ret = -EBUSY;goto err;}ret = security_binder_set_context_mgr(proc->tsk);if (ret < 0)goto err;if (binder_context_mgr_uid != -1) {if (binder_context_mgr_uid != current->cred->euid) {printk(KERN_ERR "binder: BINDER_SET_"       "CONTEXT_MGR bad uid %d != %d\n",       current->cred->euid,       binder_context_mgr_uid);ret = -EPERM;goto err;}} elsebinder_context_mgr_uid = current->cred->euid;binder_context_mgr_node = binder_new_node(proc, NULL, NULL);if (binder_context_mgr_node == NULL) {ret = -ENOMEM;goto err;}binder_context_mgr_node->local_weak_refs++;binder_context_mgr_node->local_strong_refs++;binder_context_mgr_node->has_strong_ref = 1;binder_context_mgr_node->has_weak_ref = 1;break;ret = 0;err:if (thread)thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;binder_unlock(__func__);return ret;}
    代码里,首先获得前面Binder驱动程序为ServiceManager进程创建的binder_prop结构体,并且保存在变量proc中。接着,调用函数binder_get_thread为当前线程创建一个binder_thread结构体。当前线程即为ServiceManager进程的主线程,同时它也是ServiceManager进程中的一个Binder线程。因此,在它第一次进入到Binder驱动程序时,Binder驱动程序就需要为它创建一个binder_thread结构体。函数binder_get_thread的实现如下所示:
static struct binder_thread *binder_get_thread(struct binder_proc *proc){struct binder_thread *thread = NULL;struct rb_node *parent = NULL;struct rb_node **p = &proc->threads.rb_node;while (*p) {parent = *p;thread = rb_entry(parent, struct binder_thread, rb_node);if (current->pid < thread->pid)p = &(*p)->rb_left;else if (current->pid > thread->pid)p = &(*p)->rb_right;elsebreak;}if (*p == NULL) {thread = kzalloc(sizeof(*thread), GFP_KERNEL);if (thread == NULL)return NULL;binder_stats_created(BINDER_STAT_THREAD);thread->proc = proc;thread->pid = current->pid;init_waitqueue_head(&thread->wait);INIT_LIST_HEAD(&thread->todo);rb_link_node(&thread->rb_node, parent, p);rb_insert_color(&thread->rb_node, &proc->threads);thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;thread->return_error = BR_OK;thread->return_error2 = BR_OK;}return thread;}
    函数binder_get_thread在为一个线程创建一个binder_thread结构体之前,首先会检查与该线程对应的binder_thread结构体是否已经存在。如果存在,就不用创建了,可以直接将该binder_thread结构体返回给调用者。一个进程的所有Binder线程都保存在一个binder_proc结构体的成员变量threads所描述的一个红黑树中。由于这个红黑树是以线程的PID为关键字来组织的,因为while循环就以当前线程的PID在这个红黑树中查找是否已经存在一个对应的binder_thread结构体。如果不存在,则最后的红黑树节点p为NULL,那么接下来就为当前线程创建一个binder_thread结构体,并且对它进行初始化,然后再将它添加到其宿主进程的成员变量threads所描述的一个红黑树中。
    返回到函数binder_ioctl中,全局变量binder_context_mgr_node用来描述与Binder进程间通信机制的上下文管理者相对应的一个Binder实体对象,如果它不等于NULL,那么说明前面已经有组件将自己注册为Binder进程间通信机制的上下文管理者了。由于Binder驱动程序不允许重复注册Binder进程间通信机制的上下文管理者,因此,在这种情况下,它就直接出错返回了。
    全局变量binder_context_mgr_uid用了描述注册了Binder进程间通信机制的上下文管理者的进程的有效用户ID,如果它的值不等于-1,就说明前面已经有一个进程注册了Binder进程间通信机制的上下文管理者了。在这种情况下,Binder驱动程序就需要检查当前进程的有效用户ID是否等于全局变量binder_context_mgr_uid的值。如果不等于,那么它就直接出错返回了。Binder驱动程序允许同一个进程重复使用IO控制命令BINDER_SET_CONTEXT_MGR来注册BInder进程间通信机制的上下文管理者,因为该进程前一次使用IO控制命令BINDER_SET_CONTEXT_MGR时,可能没有成功地将一个组件注册为Binder进程间通信机制的上下文管理者。这种情况是很有可能的,例如下面的方法binder_new_node就很有可能会执行失败。如果通过合法化检查,就可以调用binder_new_node为ServiceManager创建一个Binder实体对象了,并将它保存在全局变量binder_context_mgr_node中。函数binder_new_node的实现如下:
static struct binder_node *binder_new_node(struct binder_proc *proc,   void __user *ptr,   void __user *cookie){struct rb_node **p = &proc->nodes.rb_node;struct rb_node *parent = NULL;struct binder_node *node;while (*p) {parent = *p;node = rb_entry(parent, struct binder_node, rb_node);if (ptr < node->ptr)p = &(*p)->rb_left;else if (ptr > node->ptr)p = &(*p)->rb_right;elsereturn NULL;}node = kzalloc(sizeof(*node), GFP_KERNEL);if (node == NULL)return NULL;binder_stats_created(BINDER_STAT_NODE);rb_link_node(&node->rb_node, parent, p);rb_insert_color(&node->rb_node, &proc->nodes);node->debug_id = ++binder_last_id;node->proc = proc;node->ptr = ptr;node->cookie = cookie;node->work.type = BINDER_WORK_NODE;INIT_LIST_HEAD(&node->work.entry);INIT_LIST_HEAD(&node->async_todo);binder_debug(BINDER_DEBUG_INTERNAL_REFS,     "binder: %d:%d node %d u%p c%p created\n",     proc->pid, current->pid, node->debug_id,     node->ptr, node->cookie);return node;}
    第一个参数proc用了描述ServiceManager进程。第二个参数ptr和第三个参数cookie用来描述一个Binder本地对象,它们分别指向该Binder本地对象内部的一个弱引用计数对象的地址值,以及该Binder本地对象的地址值。由于与ServiceManager对应的Binder本地对象的地址值为0,因此,第二个参数ptr和第三个参数cookie的值均为指定为NULL。至此,ServiceManager就成功地将自己注册为Binder进程间通信进程的上下文管理者。函数binder_ioctl在返回用户空间之前,会将当前线程的状态位BINDER_LOOPER_STATE_NEED_RETURN清零,这样当该线程下次再进入到Binder驱动程序时,Binder驱动程序就可以将i进程间通信请求分发给它处理了。ServiceManager返回到进程的用户空间之后,接着继续调用函数binder_loop来循环等待和处理Client进程的通信请求,即等待和处理Service组建的注册请求,以及其代理对象的获取请求。

循环等待Client进程请求

    由于ServiceManager需要在系统运行期间为Service组件和Client组建提供服务,因此,它就需要通过一个无限循环来等待和处理Service组件和Client组件的进程间通信请求,这是通过binder_loop函数来实现的。binder_loop函数定义在"frameworks/base/cmds/servicemanager/binder.c"文件中:
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;        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);        if (res < 0) {            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));            break;        }        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);        if (res == 0) {            ALOGE("binder_loop: unexpected reply?!\n");            break;        }        if (res < 0) {            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));            break;        }    }}
    往binder_loop函数中传递的那个函数指针是svcmar_handler,它的代码如下所示:
int svcmgr_handler(struct binder_state *bs,                   struct binder_txn *txn,                   struct binder_io *msg,                   struct binder_io *reply){    struct svcinfo *si;    uint16_t *s;    unsigned len;    void *ptr;    uint32_t strict_policy;    int allow_isolated;//    ALOGI("target=%p code=%d pid=%d uid=%d\n",//         txn->target, txn->code, txn->sender_pid, txn->sender_euid);    if (txn->target != svcmgr_handle)        return -1;    // Equivalent to Parcel::enforceInterface(), reading the RPC    // header with the strict mode policy mask and the interface name.    // Note that we ignore the strict_policy and don't propagate it    // further (since we do no outbound RPCs anyway).    strict_policy = bio_get_uint32(msg);    s = bio_get_string16(msg, &len);    if ((len != (sizeof(svcmgr_id) / 2)) ||        memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {        fprintf(stderr,"invalid id %s\n", str8(s));        return -1;    }    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, txn->sender_euid);        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);        allow_isolated = bio_get_uint32(msg) ? 1 : 0;        if (do_add_service(bs, s, len, ptr, txn->sender_euid, allow_isolated))            return -1;        break;    case SVC_MGR_LIST_SERVICES: {        unsigned n = bio_get_uint32(msg);        si = svclist;        while ((n-- > 0) && si)            si = si->next;        if (si) {            bio_put_string16(reply, si->name);            return 0;        }        return -1;    }    default:        ALOGE("unknown code %d\n", txn->code);        return -1;    }    bio_put_uint32(reply, 0);    return 0;}
    从前面的调用过程可以知道,第一个参数bs指向前面在函数binder_open中创建的一个binder_state结构体; 第二个参数func就是上面说的svcmgr_handler函数,它是用来处理Service组件和Client组件的进程间通信请求的。由于ServiceManager进程的主线程是主动成为一个Binder线程的,因此,它就使用BC_ENTER_LOOPER协议将自己注册到BInder驱动程序中。
    代码中,首先将BC_ENTER_LOOPER协议代码写入到缓冲区readbuf中,接着调用函数binder_write将它发送到Binder驱动程序中。函数binder_write的实现如下所示:
int binder_write(struct binder_state *bs, void *data, unsigned len){    struct binder_write_read bwr;    int res;    bwr.write_size = len;    bwr.write_consumed = 0;    bwr.write_buffer = (unsigned) data;    bwr.read_size = 0;    bwr.read_consumed = 0;    bwr.read_buffer = 0;    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);    if (res < 0) {        fprintf(stderr,"binder_write: ioctl failed (%s)\n",                strerror(errno));    }    return res;}
   由于BC_ENTER_LOOPER协议是通过IO控制命令BINDER_WRITE_READ发送到Binder驱动程序中的,而IO控制命令BINDER_WRITE_READ后面跟的参数是一个binder_write_read结构体。因此,binder_write函数首先定义了一个binder_wirte_read结构体bwr,接着将data所指向的一块缓冲区作为它的输入缓冲区。接下来,将binder_write_read结构体bwr的输出缓冲区设置为空,这样,当前线程将自己注册到Binder驱动程序中之后,就会马上返回到用户空间,而不会在BInder驱动程序中等待Client进程的通信请求。由于参数data所指向的一块缓冲区的内容已经被设置为BC_ENTER_LOOPER协议代码,因此,接下来就可以调用函数ioctl将当前线程注册到BInder驱动程序中了。
    IO控制命令BINDER_WRITE_READ是由Binder驱动程序中的函数binder_ioctl负责处理的,代码如下所示:
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){int ret;struct binder_proc *proc = filp->private_data;struct binder_thread *thread;unsigned int size = _IOC_SIZE(cmd);void __user *ubuf = (void __user *)arg;binder_lock(__func__);thread = binder_get_thread(proc);if (thread == NULL) {ret = -ENOMEM;goto err;}switch (cmd) {case BINDER_WRITE_READ: {struct binder_write_read bwr;if (size != sizeof(struct binder_write_read)) {ret = -EINVAL;goto err;}if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {ret = -EFAULT;goto err;}binder_debug(BINDER_DEBUG_READ_WRITE,     "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",     proc->pid, thread->pid, bwr.write_size, bwr.write_buffer,     bwr.read_size, bwr.read_buffer);if (bwr.write_size > 0) {ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);trace_binder_write_done(ret);if (ret < 0) {bwr.read_consumed = 0;if (copy_to_user(ubuf, &bwr, sizeof(bwr)))ret = -EFAULT;goto err;}}if (bwr.read_size > 0) {ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);trace_binder_read_done(ret);if (!list_empty(&proc->todo))wake_up_interruptible(&proc->wait);if (ret < 0) {if (copy_to_user(ubuf, &bwr, sizeof(bwr)))ret = -EFAULT;goto err;}}binder_debug(BINDER_DEBUG_READ_WRITE,     "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",     proc->pid, thread->pid, bwr.write_consumed, bwr.write_size,     bwr.read_consumed, bwr.read_size);if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {ret = -EFAULT;goto err;}break;}ret = 0;        return ret;}
    代码首先还是用函数binder_get_thread来获取与当前线程对应的一个binder_thread结构体,并且保存在变量thread中。当前线程即为ServiceManager进程的主线程,前面它将ServiceManager注册为BInder进程间通信机制的上下文管理者时,Binder驱动程序已经为它创建过一个binder_thread结构体了,因此,调用函数binder_get_thread时,就可以直接获得该binder_thread结构体。copy_from_user是从用户空间传过来的一个binder_write_read结构体拷贝出来,并且保存在变量bwr中。
   

原文链接

1. http://blog.csdn.net/luoshengyang/article/details/6621566
2. 《深入理解Android 卷一》
0 0