Binder系列3 ServiceManager启动和实现

一 ServiceManager概述

Android 平台的一个基本设计理念是构造一个相对平坦的功能集合,这些功能可能会处于不同的进程中,然而却可以高效地整合到一起,实现不同的用户需求。这就必须打破过去各个孤立 App 所形成的天然藩篱。为此,Android 提供了 Binder 机制。

在 Android 中,系统提供的服务是以各种形式的 Service 存在的,这些 Service 往往会在设备启动之时,添加进 Android 系统。在上一篇文章 Binder系列2 Binder概念及相关接口和类 中,我们已经了解了 BpBinder 和 BBinder 的概念,知道他们都是继承了IBinder 接口,是实现 Binder 通信必须要实现的接口.而提供服务的 Service,要想支持 Binder 通信,同样也需要实现 IBinder 接口.事实上支持 Binder 通信的 Service 一般都是继承自 BBinder 的,也就间接实现了 IBinder 接口,举个例子:MediaPlayerService 继承自 BnMediaPlayerService,而 BnMediaPlayerService 又继承自 BnInterface<IMediaPlayerServce>,这个 BnInterface<IXXXX> 是个模板类,继承自 BBinder,几乎所有支持 Binder 通信的 Service 都是这个继承模式,也就是每个Service 实质上是一个 BBinder,他继承了 IBinder 接口,能够支持 Binder 通信,我们一般称其为一个 Binder 实体对象。

我们知道,如果某个 Client 进程,希望跨进程使用系统提供的服务,那么它就必须先拿到和某个系统 Service 对应的 Binder 代理接口,然后通过这个 Binder 代理,才能调用和使用 Server 端提供的系统服务。说白了就是,我们得先拿到一个和目标 Service 对应的合法 Binder 代理-- BpBinder。

然而,该怎么获取和系统 Service 对应的 BpBinder 呢?Android 是这样设计的:先启动一个特殊的系统服务,叫作ServiceManager Service(简称SMgr),它的基本任务有二个:一是注册服务;二是查询和获取服务。分别给 Server 端和 Client端使用.当系统 Service 在系统启动之时,会向 SMgr 注册自己,把自己的 Binder 实体对象和对应的名字通过 Binder 驱动传递给 SMgr,Binder 驱动会生成一个句柄值,然后把这个句柄值和名字传递给 SMgr,这样一来 SMgr 中就记录了系统中每个Service 对应的句柄值和名字.后续当 Clinet 进程根据 Service 名字通过 SMgr 来请求某个 Service 的 BpBinder 时,SMgr 先是根据 Service 的名字找到对应的句柄值,然后经过 Binder 驱动返回给 Client,然后 Client 进程根据这个从驱动返回过来的新的句柄值(和 SMgr 中的句柄值可能不一样)创建 BpBinder,这个 BpBinder 就是合法的 Binder 代理接口.

在此,我们有必要交代一下 Binder 句柄的作用。句柄说穿了就是个简单的整数值,它是由 Binder 驱动生成的,并由 Binder 驱动来维护,它对应着 Server 端的一个 Binder 实体对象,这个映射关系是由 Binder 驱动保证的,我们不需要担心.还有一点要注意的是,句柄只对发起端进程和 Binder 驱动有意义,A 进程的句柄直接拿到 B 进程,是没什么意义的。也就是说,不同进程中指代相同 Binder 实体的句柄值可能是不同的.为什么是这样呢,这是由句柄的生成机制决定的,以后会详细讲,现在简单阐述如下:

Binder 驱动为每个进程都维护了一个独立的引用树 refs_by_desc,这个引用树是由 binder_ref 节点链接而成的,而一个binder_ref 节点代表了一个 Binder 引用,这个引用对应着 Server 端的一个 Binder 实体对象.每当 Binder 驱动为某个进程生成一个 binder_ref 对象时,就会在 binder_ref 中的 desc 域中赋值一个递增的整数值,第一个创建的 binder_ref 的 desc 为1,第二个为2,依次递增(其中的0号引用固定留给了 SMgr),这个整数值就是前面所说的句柄.这样在某个进程内部,句柄值显然只是这个 binder_ref 在本进程中创建的先后次序而已,先创建的句柄数值小,而后创建的句柄数值大.所以对指向同一个 Binder 实体对象的不同进程中的 binder_ref 来说,由于它们在不同进程中,Binder 驱动为其创建的次序可能是不一样的,所以他们的句柄值也可能不一样,但是无需担心,驱动都会精确地为它们找到对应的 Binder 实体对象的.示意图如下:

小结以下,SMgr 就像是一个域名服务器 DNS,他里面记录了所有系统 Service 的名字和所对应的 Binder 句柄,它的核心功能就是维护好这些句柄值。后续,当 Client 进程需要获取某个系统 Service 的代理 BpBinder 时,SMgr 就会在内部,根据 Service 的名字查找到对应的句柄值,并“逻辑上”传递给 Client 进程,于是 Client 进程会得到一个新的合法的句柄值,这个新句柄值,可能在数值上和 SMgr 所记录的句柄值不同,然而,它们指代的却是同一个 Service 实体,然后 Client 根据这个新的句柄值生成一个BpBinder 代理对象,通过这个代理对象使用远端的服务.

从以上说明可以知道,无论是系统 Service 的注册,还是 Client 的查询,都需要用到 SMgr,所以如果我们要分析 Binder 框架,那么首先要了解 SMgr 的启动和实现.

二 SMgr的启动和实现

需要说明的是 SMgr 可以看成是 Binder 通信过程中的守护进程,他本身也是一个 Binder 服务,但并没有采用普通 Binder 服务进程中通过 ProsessState 和 IPCThreadState 的多线程模型来与 Binder 驱动通信,而是实现在一个独立的进程中,用 C语言实现,直接通过 ioctl 函数来和 Binder 驱动通信,并且只有一个循环 binder_loop 来进行读取和处理事务,这样的好处是简单而高效。

2.1 SMgr服务定义

 frameworks/native/cmds/servicemanager/servicemanager.rc

service servicemanager /system/bin/servicemanager
    class core animation
    user system
    group system readproc
    critical
    onrestart restart healthd
    onrestart restart zygote
    onrestart restart audioserver
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart inputflinger
    onrestart restart drm
    onrestart restart cameraserver
    onrestart restart keystore
    onrestart restart gatekeeperd
    writepid /dev/cpuset/system-background/tasks
    shutdown critical

SMgr启动入口

system/core/rootdir/init.rc

    # start essential services
    start logd
    start servicemanager // 启动servicemanager
    start hwservicemanager
    start vndservicemanager

2.2 SMgr主入口main函数

 frameworks/native/cmds/servicemanager/service_manager.c

int main(int argc, char** argv)
{
    struct binder_state *bs;
    char *driver;

    if (argc > 1) {
        driver = argv[1];
    } else {
        driver = "/dev/binder";
    }

    bs = binder_open(driver, 128*1024); //打开Binder驱动
   ........
    if (binder_become_context_manager(bs)) { //告诉Binder驱动,申请自己为系统全局的SMgr
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
    ........
    binder_loop(bs, svcmgr_handler); // 进入loop循环,等待接收其它进程的请求,并作出处理
    return 0;
}

从代码中可以看到,main 函数主要做了以下三件事情:

  • 打开 Binder 驱动:binder_open;
  • 向 Binder 驱动注册自己成为 Binder 通信机制的守护进程:binder_become_context_manager;
  • 进入无限循环,处理 client 端发来的请求:binder_loop;

2.2.1 binder_open

frameworks/native/cmds/servicemanager/binder.c

struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;

    bs = malloc(sizeof(*bs));
    ........

    bs->fd = open(driver, O_RDWR | O_CLOEXEC); //打开Binder驱动文件"dev/binder"
    ........
    //查询binder驱动的版本号
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
         ........
      goto fail_open;
    }

    bs->mapsize = mapsize; //设置映射内存大小,这里为128k
    //通过系统调用,mmap函数负责内存映射
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    ........
    return bs; //初始化binder_state完成,然后返回
    ........
}

Binder 驱动定义的 file_operations 如下:

static const struct file_operations binder_fops = {
	.owner = THIS_MODULE,
	.poll = binder_poll,
	.unlocked_ioctl = binder_ioctl,
	.compat_ioctl = binder_ioctl,
	.mmap = binder_mmap,
	.open = binder_open,
	.flush = binder_flush,
	.release = binder_release,
};

由此可知 open,ioctl 和 mmap 分别对应 Binder 驱动的 binder_open,binder_ioctl 和 binder_mmap 函数,关于 Binder 驱动的启动和加载后续会详细介绍,现在请忽略,我们只需要知道这个对应关系即可.

需要注意的是,在普通的提供 Binder 服务的进程中,一般是通过 ProcessState 的 open_driver 函数来打开 Binder 驱动的,虽然打开形式不一样,但是走到 Binder 驱动里,执行的却是同样的 binder_open 函数,如下:

static int binder_open(struct inode *nodp, struct file *filp)
{
	struct binder_proc *proc; //对应的进程结构体
	struct binder_device *binder_dev;//对应的binder_device
	........
	proc = kzalloc(sizeof(*proc), GFP_KERNEL);//在驱动层创建binder_proc
	........
	INIT_LIST_HEAD(&proc->todo); //初始化进程的todo等待队列
	........
   //获取binder_device,这个binder_device在Binder驱动初始化的时候生成
	binder_dev = container_of(filp->private_data, struct binder_device,
				  miscdev);
    //每个进程的context都对应着同一个binder_dev的context
	proc->context = &binder_dev->context;
	binder_alloc_init(&proc->alloc);

	binder_stats_created(BINDER_STAT_PROC);
	proc->pid = current->group_leader->pid;
	INIT_LIST_HEAD(&proc->delivered_death);
	INIT_LIST_HEAD(&proc->waiting_threads);
    //把代表当前进程的binder_proc赋值给filp的private_data
    //这个非常重要,以后Binder驱动就是根据这个域找到对应的是哪个进程的.
	filp->private_data = proc;

	mutex_lock(&binder_procs_lock);
    //把这个binder_proc链入到全局的 binder_procs中
	hlist_add_head(&proc->proc_node, &binder_procs);
	mutex_unlock(&binder_procs_lock);
	........
	return 0;
}

这个 binder_open 函数主要的作用是,生成代表本进程的 binder_proc,并对其进行初始化,然后把新创建的这个 binder_proc 链入到全局的 binder_procs 中,其中需要注意的是 filp->private_data = proc; 即把这个 binder_proc 赋值给了 filp 的 private_data,以后 Binder 驱动会根据 filp 的这个域找到请求发起端对应的是哪一个进程.

接下来看 binder_ioctl 函数的实现,如下:

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	int ret;
    //之前介绍过,通过这个private_data域找到对应的进程
	struct binder_proc *proc = filp->private_data; 
	struct binder_thread *thread;
	unsigned int size = _IOC_SIZE(cmd);
	void __user *ubuf = (void __user *)arg;
	........
	ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	if (ret)
		goto err_unlocked;

	thread = binder_get_thread(proc);//这个方法很重要,从proc中获取线程,如果没有则创建
	........
	switch (cmd) {//解析命令,根据不用的命令,执行不同的操作,这里关注BINDER_VERSION命令.
	case BINDER_WRITE_READ:
		ret = binder_ioctl_write_read(filp, cmd, arg, thread);
		if (ret)
			goto err;
		break;
	case BINDER_SET_MAX_THREADS: {
		int max_threads;
		if (copy_from_user(&max_threads, ubuf,
				   sizeof(max_threads))) {
			ret = -EINVAL;
			goto err;
		}
		binder_inner_proc_lock(proc);
		proc->max_threads = max_threads;
		binder_inner_proc_unlock(proc);
		break;
	}
	case BINDER_SET_CONTEXT_MGR://申请自己为全局的SMgr
		ret = binder_ioctl_set_ctx_mgr(filp);
		if (ret)
			goto err;
		break;
	case BINDER_THREAD_EXIT:
		binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
			     proc->pid, thread->pid);
		binder_thread_release(proc, thread);
		thread = NULL;
		break;
	case BINDER_VERSION: {
		struct binder_version __user *ver = ubuf;
		if (size != sizeof(struct binder_version)) {
			ret = -EINVAL;
			goto err;
		}
		if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
			     &ver->protocol_version)) {
        //很简单的操作,把当前binder驱动的版本信息通过put_user命令返回给用户空间
			ret = -EINVAL;
			goto err;
		}
		break;
	}
	case BINDER_GET_NODE_DEBUG_INFO: {
		struct binder_node_debug_info info;

		if (copy_from_user(&info, ubuf, sizeof(info))) {
			ret = -EFAULT;
			goto err;
		}

		ret = binder_ioctl_get_node_debug_info(proc, &info);
		if (ret < 0)
			goto err;

		if (copy_to_user(ubuf, &info, sizeof(info))) {
			ret = -EFAULT;
			goto err;
		}
		break;
	}
	default:
		ret = -EINVAL;
		goto err;
	}
	ret = 0;
err:
	if (thread)
		thread->looper_need_return = false;
	wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
	if (ret && ret != -ERESTARTSYS)
		pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
err_unlocked:
	trace_binder_ioctl_done(ret);
	return ret;
}

binder_ioctl 函数是用户空间和 Binder 驱动通信的最主要方式,这里的每个命令在 Binder系列1 Binder总体设计思想 中已经做了详细说明,在此不再赘述,这个函数里边主要是对各个命令的具体操作.这里使用到的是 BINDER_VERSION 命令,很简单就是通过 put_user 把当前 Binder 驱动的版本号,返回给用户空间.

接下来看 binder_mmap 函数,如下:

static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
	int ret;
	struct binder_proc *proc = filp->private_data; //还是通过private_data域找到proc
	const char *failure_string;
	........
	if ((vma->vm_end - vma->vm_start) > SZ_4M)
		vma->vm_end = vma->vm_start + SZ_4M;//限定映射的内存空间不得超过4M
	........
	vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
	vma->vm_ops = &binder_vm_ops;
	vma->vm_private_data = proc;
    // 将进程虚拟地址空间和内核虚拟地址空间, 映射到同一个物理地址空间
	ret = binder_alloc_mmap_handler(&proc->alloc, vma);

	return ret;

err_bad_arg:
	pr_err("binder_mmap: %d %lx-%lx %s failed %d\n",
	       proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
	return ret;
}

binder_mmap 里边最重要的操作是调用 binder_alloc_mmap_handler 函数,这个函数在这里不再详细阐述,我们只需要知道这个函数的作用是为进程间通信的进程分配内核虚拟地址空间,并且为内核虚拟地址空间和用户虚拟地址空间申请物理内存,并建立映射关系,如下图:

因为进程的用户空间虚拟内存地址和内核空间虚拟内存地址被映射到了同一片物理内存中,这样就实现了 Binder 驱动只需要对位于内核空间的虚拟内存地址的操作,就间接也反馈到了用户空间的虚拟内存地址,这个就是 Binder 机制"一次拷贝"的原因.详细内容以后会讲.

这样 binder_open 函数基本就讲完了,就是通过 open 打开 Binder 驱动,并通过 ioctl 函数查询 Binder 驱动版本信息,然后通过mmap 函数申请物理内存并建立内存映射关系.

2.2.2 申请自己为全局的SMgr

回到 main 函数,接下来看 binder_become_context_manager 是如何操作让自己为全局 SMgr 的,如下:

int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

这个函数很简单,就是通过 ioctl 函数向驱动发送了 BINDER_SET_CONTEXT_MGR 命令,继续看 Binder 驱动中的 binder_ioctl 函数对这个命令的处理:

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	........
	switch (cmd) {	
	case BINDER_SET_CONTEXT_MGR:
		ret = binder_ioctl_set_ctx_mgr(filp);
		if (ret)
			goto err;
		break;
	........
}

执行 binder_ioctl_set_ctx_mgr 函数,如下:

static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
	int ret = 0;
    //通过filp的private_data域找到是哪个进程要申请成为SMgr的
	struct binder_proc *proc = filp->private_data;     
	struct binder_context *context = proc->context;
	struct binder_node *new_node;//SMgr进程的binder_node
	kuid_t curr_euid = current_euid();
	........
    //为这个SMgr创建binder_node,注意这个null参数,这个是SMgr和普通Binder实体的区别所在
	new_node = binder_new_node(proc, NULL);     
	........
	binder_node_lock(new_node);
   //初始化binder_node的各个域
	new_node->local_weak_refs++;
	new_node->local_strong_refs++;
	new_node->has_strong_ref = 1;
	new_node->has_weak_ref = 1;
    //把这个binder_node节点保存到进程的binder_context中,这个binder_context是全局共享的
    //其它进程也可以通过自己的binder_context来获取这个binder_context_mgr_node
	context->binder_context_mgr_node = new_node;
	binder_node_unlock(new_node);
	binder_put_node(new_node);//释放临时引用
out:
	mutex_unlock(&context->context_mgr_node_lock);
	return ret;
}

代码意思很明确,为整个系统的上下文管理器 SMgr 专门生成一个 binder_node 节点,并记入进程 binder_proc 的binder_context的 binder_context_mgr_node 中,我们回看之前分析过的 binder_open 函数如下:

binder_dev = container_of(filp->private_data, struct binder_device, miscdev);
proc->context = &binder_dev->context;

以上代码可知,每个进程的 context 域,实际上都是全局的 binder_dev 的 context 域,即 proc->context = &binder_dev->context;所以当 SMgr 进程向驱动发送 BINDER_SET_CONTEXT_MGR 命令,驱动执行完这个函数后,每个其它进程都可以通过他的 context 域找到 binder_context_mgr_node 节点,然后就可以通过这个节点找到对应的 SMgr,然后执行查询或添加服务等操作.

2.2.3 binder_loop循环

我们重新回到 SMgr 的 main() 函数,接下来的 binder_loop() 会先向 Binder 驱动发出 BC_ENTER_LOOPER 命令,接着进入一个无限 for 循环,不断调用 ioctl() 读取从 Binder 驱动发送过来的数据,接着解析这些数据。参考代码如下:

frameworks/native/cmds/servicemanager/binder.c

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;//构造BINDER_WRITE_READ命令的数据binder_write_read
    uint32_t readbuf[32];
    bwr.write_size = 0;//write_size置为0表示只读不写
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(uint32_t));
  //向Binder驱动发送BC_ENTER_LOOPER命令,通知Binder驱动本线程进入loop状态
    for (;;) {//无限for循环
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
        //通过ioctl读取来自Binder驱动的数据
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        ........
     // 调用binder_parse函数解析读取的数据
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        ........
    }
}

注意 binder_loop() 的参数 func,它的值是 svcmgr_handler() 函数指针。而且这个参数会进一步传递给 binder_parse(),解析数据的时候会用到。

binder_loop() 中发出 BC_ENTER_LOOPER 命令的目的,是为了告诉 Binder 驱动“本线程要进入循环状态了”。在 Binder 驱动中,凡是用到跨进程通信机制的线程,都会对应一个 binder_thread 节点。这里的 BC_ENTER_LOOPER 命令会导致这个节点的looper 状态发生变化,参考 binder_ioctl 函数中对 BC_ENTER_LOOPER 的操作如下:

int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;
    int res;

    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) data;
    bwr.read_size = 0;//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;
}
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	........
	switch (cmd) {	
	case BC_ENTER_LOOPER:
		if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
			  thread->looper |= BINDER_LOOPER_STATE_INVALID;
			  binder_user_error("%d:%d ERROR: BC_ENTER_LOOPER called after BC_REGISTER_LOOPER\n",
					proc->pid, thread->pid);
		}
		thread->looper |= BINDER_LOOPER_STATE_ENTERED;
		break;
	........
}

有关 binder_thread 的细节,也会在后续做详细介绍,这里只需要知道,发送 BC_ENTER_LOOPER 命令的作用是在驱动中会把当前的 binder_thread 的 looper 变量置为 BINDER_LOOPER_STATE_ENTERED,这个表示当前线程进入循环状态了.

binder_loop() 声明了一个 128 字节的 buffer(即uint32_t readbuf[32]),每次用 BINDER_WRITE_READ 命令从驱动读取一些内容,并把这些内容作为参数传入 binder_parse() 函数进行解析。解析的时候,会回调其 func 参数(binder_handler func)指代的回调函数,即前文说到的 svcmgr_handler() 函数.binder_loop() 就这样一直无限循环下去,不断读取从 Binder 驱动发送过来的数据,然后解析,再读取,再解析.这个就是整个 SMgr 的工作状态。接下来重点研究 binder_parse 函数是怎么解析从驱动读取来的数据的.

2.2.4 binder_parse

在 binder_loop() 进入 for 循环之后,最显眼的就是那句 binder_parse() 了。binder_parse() 负责解析从 Binder 驱动读来的数据,其代码截选如下:

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    int r = 1;
    uintptr_t end = ptr + (uintptr_t) size;
    while (ptr < end) {
        uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);
        switch(cmd) {
       ........
        case BR_TRANSACTION: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            ........
            binder_dump_txn(txn);//打印txn
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;
                bio_init(&reply, rdata, sizeof(rdata), 4);//初始化reply
                bio_init_from_txn(&msg, txn);//把msg与txn建立关联
                res = func(bs, txn, &msg, &reply);
                if (txn->flags & TF_ONE_WAY) {//异步操作不需要回复,直接释放缓存空间
                    binder_free_buffer(bs, txn->data.ptr.buffer);
                } else {//同步操作需要回复,把reply作为参数返回给请求进程
                    binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
                }
            }
            ptr += sizeof(*txn);
            break;
        }
        ........        
        }
    }
    return r;
}

从驱动层读取到的数据,实际上是以 BR 命令 + 数据形式连续存放的数据。不同 BR 命令对应不同长度的数据。如下表所示:

BR命令

需进一步读取的uint32数

BR_NOOP

0

BR_TRANSACTION_COMPLETE

0

BR_INCREFS

2

BR_ACQUIRE

2

BR_RELEASE

2

BR_DECREFS

2

BR_TRANSACTION

sizeof(binder_transaction_data) / sizeof(uint32_t)

BR_REPLY

sizeof(binder_transaction_data) / sizeof(uint32_t)

BR_DEAD_BINDER

1

BR_FAILED_REPLY

0

BR_DEAD_REPLY

0

每次 ioctl() 操作所读取的数据,可能会包含多个 BR 命令,所以 binder_parse() 需要用一个 while 循环来解析 buffer 中所有的 BR命令。我们画个示意图,如下:

图中的 buffer 中含有3条 BR 命令,分别为 BR_TRANSACTION、BR_TRANSACTION_COMPLETE、BR_NOOP。一般而言,我们最关心的是 BR_TRANSACTION 命令,因此前文截选的 binder_parse() 代码,主要摘录了处理B R_TRANSACTION 命令的代码,该命令的命令号之后跟着的是一个 binder_transaction_data 结构的数据,这个结构体在 Binder系列1  Binder总体设计思想中已经有过详细介绍,在此不再赘述.为了解析数据,binder_parse() 声明了两个类型为 binder_io 的局部变量:msg 和 reply。从 binder_io 这个类型的名字,我们就可以看出,要用它来处理从 Binder 驱动传递过来的数据了。其实,为了便于读取 binder_io 所指代的内容,SMgr 中提供了一系列以 bio_ 打头的辅助函数。在读取实际数据之前,我们必须先调用 bio_init_from_txn(),把binder_io 变量(比如msg变量)和 binder_transaction_data 所指代的缓冲区联系起来。 

struct binder_io
{
    char *data;            /* pointer to read/write from */
    binder_size_t *offs;   /* array of offsets */
    size_t data_avail;     /* bytes available in data buffer */
    size_t offs_avail;     /* entries available in offsets array */

    char *data0;           /* start of data buffer */
    binder_size_t *offs0;  /* start of offsets buffer */
    uint32_t flags;
    uint32_t unused;
};
void bio_init_from_txn(struct binder_io *bio, struct binder_transaction_data *txn)
{
    bio->data = bio->data0 = (char *)(intptr_t)txn->data.ptr.buffer;
    bio->offs = bio->offs0 = (binder_size_t *)(intptr_t)txn->data.ptr.offsets;
    bio->data_avail = txn->data_size;
    bio->offs_avail = txn->offsets_size / sizeof(size_t);
    bio->flags = BIO_F_SHARED;
}

由以上代码可知 bio 的 data 指向的是 txn->data.ptr.buffer,而 bio 的 offs 指向的是 txn->data.ptr.offsets,也就是在 SMgr 进程中为了便于对 binder_transaction_data 数据的处理,自定义了 binder_io,并把 binder_transaction_data 转换成了便于操作的binder_io.

我们先来看看 func 函数把数据处理过后,SMgr 是怎么操作的,如下:

         if (txn->flags & TF_ONE_WAY) {
                    binder_free_buffer(bs, txn->data.ptr.buffer);
                } else {
                    binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
                }

判断 txn 的 flags 是否含有 IF_ONE_WAY 标志位,如果是的话,也就表示异步操作,不需要回复了,那么直接调用binder_free_buffer 把缓存区的数据释放掉,如果不是的话,则表示同步操作,还需要给请求者返回数据,则调用binder_send_reply 函数,把应答数据 reply 返回给请求者.

接下来看 func 函数是怎么处理数据的,也就是 svcmgr_handler 函数,如下:

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    struct svcinfo *si;
    uint16_t *s;
    size_t len;
    uint32_t handle;
    uint32_t strict_policy;
    int allow_isolated;
    ........
    s = bio_get_string16(msg, &len);
    ........
    switch(txn->code) {
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
        if (!handle)
            break;
        bio_put_ref(reply, handle);
        return 0;

    case SVC_MGR_ADD_SERVICE:
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        handle = bio_get_ref(msg);
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        if (do_add_service(bs, s, len, handle, txn->sender_euid,
            allow_isolated, txn->sender_pid))
            return -1;
        break;

    case SVC_MGR_LIST_SERVICES: {
        uint32_t 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;
}

在分析这个函数之前,需要先介绍下 svclist 这个链表,因为 svcmgr_handler 主要是对 svclist 的处理.svclist 链表中记录着所有通过 SMgr 注册的实名 Binder 实体对应的句柄信息,这些句柄被封装到一个 svcinfo 结构中,然后链接成一条单向链表,我们不妨称这条链表为“服务向量表”。示意图如下:

链表节点类型为svcinfo。

struct svcinfo
{
    struct svcinfo *next;//用于链接的指针
    uint32_t handle;//句柄值
    struct binder_death death;
    int allow_isolated;
    size_t len;
    uint16_t name[0];//Binder实体的名字
};

我们可以认为每一个 svcinfo 节点都表示一个 Binder 代理,里边记录了这个 Binder 代理的句柄值 handle,Binder 实体对应的名字 name 等,日后,当 Client 通过 SMgr 调用 getService() 获取系统服务的 Binder 代理接口时,SMgr 就会搜索这张“服务向量表”,查找是否有对应的 svcinfo 节点能和用户传来的服务名相匹配,如果能查到,就会把对应的句柄值通过 Binder 驱动传递给Client,然后 Binder 驱动会生成一个新的句柄值返回给 Client,然后 Client 根据这个新句柄值生成 BpBinder 代理,在 Client 中是以 sp<IBinder> 接口形式表示,这个 BpBinder 代理接口在远端对应的实体就是“目标Service实体”,也叫 Binder 实体。如此一来,系统中就会出现如下关系:

接下来看 svcmgr_handler 函数,这个函数主要是根据 txn 的 code 值,也就是函数的编号,处理不同的业务操作,比如服务查询和服务注册,分别对应 SVC_MGR_GET_SERVICE 和 SVC_MGR_ADD_SERVICE,具体的执行函数是 do_find_service 和do_add_service,先看服务查询 SVC_MGR_GET_SERVICE,先调用 do_find_service 获取句柄值,代码如下:

uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid)
{
    struct svcinfo *si = find_svc(s, len);//调用find_svc,根据名字找到对应的svcinfo节点
    if (!si || !si->handle) {
        return 0;
    }
    if (!si->allow_isolated) {
        // If this service doesn't allow access from isolated processes,
        // then check the uid to see if it is isolated.
        uid_t appid = uid % AID_USER;
        if (appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END) {
            return 0;
        }
    }
    if (!svc_can_find(s, len, spid, uid)) {
        return 0;
    }
    return si->handle;//返回这个Binder服务在SMgr进程中的句柄值
}

很简单,对代码已经添加注释了,接下来看 find_svc 函数,如下:

struct svcinfo *find_svc(const uint16_t *s16, size_t len)
{
    struct svcinfo *si;

    for (si = svclist; si; si = si->next) {//遍历svclist链表,找到匹配名字的svcinfo节点
        if ((len == si->len) &&
            !memcmp(s16, si->name, len * sizeof(uint16_t))) {
            return si;
        }
    }
    return NULL;
}

SMgr 根据 Binder 服务的名字查询 svclist 表,如果找到匹配的 svcinfo 后,返回这个 svcinfo 的句柄值 handle,找到句柄值后,需要把这个值返回给请求端,怎么返回呢?通过 reply 返回.所以接着调用 bio_put_ref 函数,对 reply 做操作,从 reply 所关联的 data 域中划分出一个 flat_binder_object 大小的区域,然后开始对这个区域写值.主要目的就是把这个句柄形式的 Binder 结构,跨进程传输给调用 getService 接口的 Client 端.

最后调用 binder_send_reply 函数把 reply 发送给 Clent 端.我们之前分析过 Binder 在传输中的表现形式就是这个flat_binder_object,这个 flat_binder_object 传输到 Client 端的时候会经过 Binder 驱动,然后 Binder 驱动根据这个 flat_binder_object 的句柄找到对应的 binder_node,然后会在目标进程 Client 中重新生成一个新的 binder_ref 指向这个binder_node,然后会把新生成的 binder_ref 的新的一个句柄值 handle,传输给 Client 端,然后 Cilent 端就可以用这个句柄值生成一个 BpBinder 代理了,这个代理就和 SMgr 中的句柄指向的是同一个 Binder 实体,但是他们的句柄值可能不一样.

void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       binder_uintptr_t buffer_to_free,
                       int status)
{
    struct {
        uint32_t cmd_free;
        binder_uintptr_t buffer;
        uint32_t cmd_reply;
        struct binder_transaction_data txn;
    } __attribute__((packed)) data;
     //为了便于操作,新定义一个数据结构data,包含两条命令
    data.cmd_free = BC_FREE_BUFFER;//先是free buffer
    data.buffer = buffer_to_free;
    data.cmd_reply = BC_REPLY;//然后发送BC_REPLY
    data.txn.target.ptr = 0;
    data.txn.cookie = 0;
    data.txn.code = 0;
    if (status) {
        data.txn.flags = TF_STATUS_CODE;
        data.txn.data_size = sizeof(int);
        data.txn.offsets_size = 0;
        data.txn.data.ptr.buffer = (uintptr_t)&status;
        data.txn.data.ptr.offsets = 0;
    } else {
        data.txn.flags = 0;
        data.txn.data_size = reply->data - reply->data0;
        data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
        data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
        data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
    }
    binder_write(bs, &data, sizeof(data));
  //最后把这些数据组织到binder_write_read中通过BINDER_WRITE_READ发送
}

接下来看 SVC_MGR_ADD_SERVICE 操作,先调用 bio_get_ref(msg),从 msg 中获取要添加的 Binder 代理的句柄值,接下来执行添加操作函数 do_add_service,如下:

int do_add_service(struct binder_state *bs,
                   const uint16_t *s, size_t len,
                   uint32_t handle, uid_t uid, int allow_isolated,
                   pid_t spid)
{
    struct svcinfo *si;//声明一个svcinfo节点
    if (!handle || (len == 0) || (len > 127))
        return -1;
    if (!svc_can_register(s, len, spid, uid)) {
        ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
             str8(s, len), handle, uid);
        return -1;
    }
    si = find_svc(s, len);//调用find_svc根据名字查询是否已经存在
    if (si) {//异常情况
        if (si->handle) {
            ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
                 str8(s, len), handle, uid);
            svcinfo_death(bs, si);//如果已经存在,则release这个句柄
        }
        si->handle = handle;//赋值新的handle
    } else {
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
        ........
     //初始化si
        si->handle = handle;
        si->len = len;
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
        si->name[len] = '\0';
        si->death.func = (void*) svcinfo_death;
        si->death.ptr = si;
        si->allow_isolated = allow_isolated;
        si->next = svclist;
        svclist = si;//把这个svcinfo节点添加到svclist链表中
    }
    binder_acquire(bs, handle);
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}

就是生成一个 svcinfo,并把要添加的 Binder 代理的句柄值,连同名字等信息一起赋值给这个 svcinfo,最后添加进链表 svclist中,以方便以后的 Client 进程查询.

三 时序图

 

四 总结

SMgr 集中管理系统内的所有服务,它对外提供服务注册和服务查询两大功能接口,Service 可以向 SMgr 注册服务,Client 可以通过 SMgr 查询服务.类似 DNS,Client 可以通过字符串形式的服务名称来查询所需要的服务,简单方便.SMgr 虽然也是一个Binder 服务,但是它和其它普通的 Binder 服务还是不一样的,它是用 C 语言实现在一个单独的进程中的,通过 ioctl 直接来和 /dev/binder 驱动交互, 使用的 servicemanager/binder.c文件, 本质还是调用 android/binder.c, 不过是在 android/binder.c 的基础上做了一层封装,它并没有使用到普通 Binder 服务使用到的 ProcessState 和 IPCThreadState,大家需要注意其中的区别.

SMgr 启动流程:

  1. 打开 Binder 驱动,并调用 mmap() 方法分配128k的内存映射空间:binder_open();
  2. 通知 Binder 驱动使其成为守护进程:binder_become_context_manager();
  3. 进入循环状态,等待 Client 端的请求:binder_loop()。

SMgr 最核心的两个功能为服务的注册和查询:

  • 注册服务:记录服务名和 handle 信息,保存到 svclist 列表;
  • 查询服务:根据服务名查询相应的的 handle 信息。

以上是 SMgr 最核心的流程,其中还有很多细节比如添加服务的时候会涉及到权限认证,以及各种对 binder_io 操作的函数,都没有介绍,这个读者可以对照代码自己研究,如有不同观点欢迎留言交流. 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值