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

前言

    我觉得搞懂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的定义如下所示:
#define	BINDER_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;
			}
		} else
			binder_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;
		else
			break;
	}
	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;
		else
			return 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 卷一》

你可能感兴趣的:(Android进程间通信(IPC)Binder机制——Service Manager)