我觉得搞懂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 Manager的源代码位于“frameworks/native/cmds/servicemanager"目录下,主要由binder.h、binder.c和service_manager.c三个文件组成。Service Manager的入口位于service_manager.c文件的main函数:
- 调用函数binder_open打开设备文件/dev/binder,并且将该文件通过mmap映射到本进程的地址空间。
- 调用函数binder_become_context_manager告诉Binder驱动程序自己是Binder上下文管理者。
- 调用函数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中。
原文链接
2. 《深入理解Android 卷一》