前言
我们都知道Android应用程序由Activity、Service、Broadcast Receiver、Content Provider四大组件构成的,他们可能运行在不同进程中,还有Android系统中还有各种服务,例如Actiivty管理服务ActivityManagerService、电源管理服务PowerManagerService、显示管理服务DisplayManagerService、包管理服务PackageManagerService等服务,这些服务在都在系统进程System中,应用程序通过上下文Context的getSystemService()方法轻松获取那些服务的功能,这是怎么做到的呢?这其中就是通过Binder跨进程通讯机制来实现的。
一.Binder机制是什么,在Android中扮演什么角色
Binder机制是Android系统跨进制通讯的一种机制,与传统的跨进程通讯如Socket、Share Memory、Signal相比,它在进程间传输数据中,只需执行一次复制操作,提高了效率节省了内存,并且Binder采用了CS架构,通讯更加稳定,提供服务的进程叫做Server进程,而访问服务的叫做Client进程,Server进程和Client进程的通讯需要依靠Binder驱动程序中的一个/dev/binder设备文件。在Server组件服务启动的时候,会将自己注册到Service Manager中,以便Client组件进程可以通过Service Manager找到Service组件服务。Service Manager是Binder进程间通讯的管理者,它是一种特殊的Service服务组件。

如上图所示,我们可以看出Client、Service、Service Manager都运行在用户进程,而Binder驱动程序运行在 内核空间,其中,Service Manager和Binder驱动程序由系统负责提供,而Client和Service组件由应用程序来实现。Client、Service和Service Manager均是通过系统调用叩open()、mmap()、ioctl()方法来访问设备文件/dev/ binder,从而实现与Binder驱动程序的交互,而交互的目的就是为了能够间接地执行进程间通信。
这里先简单介绍下Binder机制的整体流程,先有个概念先,后面再逐步的讲解
Binder机制有三个层次,Java Binder、Native Binder、kernel Binder。Java Binder依赖于Native Binder, Native Binder依赖于Kernel Binder。先介绍下Service Manager这个进程,这个Service Manager和Java层的ServiceManager不同,Java层的是一个管理类可以添加注册查询服务,通过Serive Manager Binder大管家的代理对象去添加注册查询服务的。代理对象相当于Client端。而实际的Service Manager就是Serivce端,它的代码路径为.\frameworks\native\cmds\servicemanager\service_manager.c 。它是个单独的程序开机的时候由init进程启动的。管理着所有的Service组件,如前面的ActivityManagerService等各种服务,并想Client提供获取Service组件代理对象的服务,。了解了这个背景知识我们接下来我们分析下整体流程:
1.首先需要去注册服务,如ActivityManagerService服务,注册服务的时候会先获取Service Manager的代理对象IServiceManager,把AMS服务的名称、Binder对象等封装成Parcel对象,通过ioctl通讯会把数据传给Binder驱动程序。
2.Binder驱动层会根据代理对象的句柄值(Service Manager代理对象的句柄值默认为0)去寻找驱动层的Service Manager的实体对象,因为Service Manager 在Android系统启动的时候已经向Binder驱动程序注册为Binder上下文管理者,所以肯定能找到;从前面流程我们知道会传AMS Binder对象到驱动层,驱动层会去寻找有没有AMS对对应的实体对象,如果没有,就会去创建一个AMS的实体对象,并保存在一个红黑树中,并且会创建AMS的引用对象。
3.Binder驱动程序根据Service Manager实体对象去找对应的服务端程序,即之前提的service_manager.c所运行的程序,这个程序在启动的时候就有个循环一直等待Binder驱动程序的消息。service_manager.c程序收到注册
4.Service_manager.c收到注册AMS服务的消息后,会把驱动程序传给它AMS的句柄值封装成一个binder_object对象,并把对象加入到链表中。然后返回到Binder驱动程序。
5.Binder程序收到注册成功的消息后会发消息给调用端程序,调用端程序收到消息,注册流程就结束了。
获取对应的服务流程和注册服务流程相似,这里先不介绍了。接下来具体分析怎么注册服务。
二、Binder机制的具体流程分析
2.1.Service Manager服务的启动流程
在前面提到Service Manager服务进程作为Binder机制的大管家扮演者很重要的角色,所以我们先分析下这个程序,ServiceManager是init进程负责启动的,具体是在解析init.rc配置文件时启动的,init进程是在系统启动时启动的,因此ServiceManager亦是如此。
ServiceManager的启动脚本在servicemanager.rc中, frameworks/native/cmds/servicemanager/
service servicemanager /system/bin/servicemanager
class core animation
user system //1
group system readproc
critical //2
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
service用于通知init进程创建名为servicemanager的进程,这个servicemanager进程执行程序的路径为/system/bin/servicemanager。注释1的关键字user说明servicemanager是以用户system的身份运行的,注释2处的critical说明servicemanager是系统中的关键服务,关键服务是不会退出的,如果退出了,系统就会重启,当系统重启时就会启动用onrestart关键字修饰的进程,比如zygote、media、surfaceflinger等等。servicemanager的入口函数service_manager.c
//service_manager.c程序
int main(int argc, char** argv)
{
struct binder_state *bs;//1
union selinux_callback cb;
char *driver;
if (argc > 1) {
driver = argv[1];
} else {
driver = "/dev/binder";
}
bs = binder_open(driver, 128*1024);//2
...
if (binder_become_context_manager(bs)) {
//3
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
...
if (getcon(&service_manager_context) != 0) {
ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
abort();
}
binder_loop(bs, svcmgr_handler);//4
return 0;
}
注释1处的binder_state结构体用来存储binder的三个信息:
struct binder_state
{
int fd; //binder设备的文件描述符
void *mapped; //binder设备文件映射到进程的地址空间
size_t mapsize; //内存映射后,系统分配的地址空间的大小,默认为128KB
};
main函数主要做了三件事:
1.注释2处调用binder_open函数用于打开binder设备文件,并申请128k字节大小的内存空间。
2.注释3处调用binder_become_context_manager函数,将servicemanager注册成为Binder机制的上下文管理者。
3.注释4处调用binder_loop函数,循环等待和处理client端发来的请求。
现在详细介绍下这三部分的知识
2.1.1 打开和映射Binder设备文件
Binder_open函数用来打开设备文件/dev/binder,并且将他映射到进程的地址空间,它的实现如下:
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
struct binder_state *bs;
struct binder_version vers;
//分配 binder_state空间
bs = malloc(sizeof(*bs));
if (!bs) {
errno = ENOMEM;
return NULL;
}
//打开设备文件
bs->fd = open(driver, O_RDWR | O_CLOEXEC);//1
if (bs->fd < 0) {
fprintf(stderr,"binder: cannot open %s (%s)\n",
driver, strerror(errno));
goto fail_open;
}
//获取Binder的version
if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
(vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
//2
fprintf(stderr,
"binder: kernel driver version (%d) differs from user space version (%d)\n",
vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
goto fail_open;
}
//mapszie 为128K
bs->mapsize = mapsize;
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);//3
if (bs->mapped == MAP_FAILED) {
fprintf(stderr,"binder: cannot map device (%s)\n",
strerror(errno));
goto fail_map;
}
return bs;
fail_map:
close(bs->fd);
fail_open:
free(bs);
return NULL;
}
注释1处用于打开binder设备文件,Binder驱动程序中的binder_open就会调用,它会为当前进程创建一个binder_proc结构体,用来描述当前进程的Binder进程间的通讯。
注释2处的ioctl函数用于获取Binder的版本,如果获取不到或者内核空间和用户空间的binder不是同一个版本就会直接goto到fail_open标签,释放binder的内存空间。
注释3处调用mmap函数进行内存映射,通俗来讲就是将binder设备文件映射到进程的地址空间,地址空间的大小为mapsize,也就是128K。映射完毕后会将地址空间的起始地址和大小保存在binder_state结构体中的mapped和mapsize变量中.
kernel/goldfish/drivers/staging/android/binder.c
//binder 驱动程序中代码
static int binder_open(struct inode *nodp, struct file *filp)
{
//代表Binder进程
struct binder_proc *proc;//1
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
current->group_leader->pid, current->pid);
//分配binder_proc内存空间
proc = kzalloc(sizeof(*proc), GFP_KERNEL);//2
if (proc == NULL)
return -ENOMEM;
//接下来对binder_proc结构体进行初始化操作
get_task_struct(current);
//任务控制块
proc->tsk = current;
INIT_LIST_HEAD(&proc->todo);
init_waitqueue_head(&proc->wait);
//默认优先级
proc->default_priority = task_nice(current);
//binder同步锁
binder_lock(__func__);
//将当前的binder_proc加入到binder驱动程序全局的hash队列binder_proces中
binder_stats_created(BINDER_STAT_PROC);
hlist_add_head(&proc->proc_node, &binder_procs);
//进程pid
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
//将proc结构体保存在参数filp成员变量private_data中,参数filep指向一个打开、//文件结构体,当进程调用函数open打开设备文件/dev/binder之后,内核会返回一个文件句
//柄,这个文件句柄就是和参数filep关联在一起的,因此后面进程使用mmap或者ioctl和
//驱动程序交互时,就可以用成员变量private_data找到binder_proc结构体
filp->private_data = proc;//3
//binder同步锁释放
binder_unlock(__func__);
...
return 0;
}
上面代码已经注释很清楚了,调用open,Binder驱动程序主要创建binder_proc结构体,并加入到全局hash队列中,无论是在Service组件或者是在Client程序中调用open打开设备文件/dev/binder都会为其创建一个binder_proc结构体代表目标进程。
2.1.2注册成为Binder上下文管理者
Service Manager要成为Binder进程间通信机制的上下文管理者,就必须通过IO控制命令BINDER_SET_CONTEXT_MGR将自己注册到Binder驱动程序中,通过函数binder_become_context_manager来实现,代码如下:
int binder_become_context_manager(struct binder_state *bs)
{
return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
BINDER_SET_CONTEXT_MGR是一个整型参数,用来描述一个和Service Manager对应Binder本地对象的地址值,由于Service Manger对应的Binder本地对象是一个虚拟对象,其地址值为0,所以后面一个参数传0就可以了。
最终会调用Binder驱动程序的binder_ioctl函数
kernel/goldfish/drivers/staging/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
//获取binder_proc结构体
struct binder_proc *proc = filp->private_data; //1
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
trace_binder_ioctl(cmd, arg);
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
binder_lock(__func__);
//调用函数binder_get_thread为当前线程创建一个binder_thread结构体,当前线程即Service Manager进程的主线程,同时它也是Binder线程
thread = binder_get_thread(proc);//2
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
switch (cmd) {
...
case BINDER_SET_CONTEXT_MGR:
//binder_context_mgr_node不等于NULL说明已经注册过了
if (binder_context_mgr_node != NULL) {
//3
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;
//全局变量binder_context_mgr_uid用来描述注册了binder上下文管理者的进程的有效用户ID,不等于-1,说明已经有一个进程注册过了上下文管理者
if (binder_context_mgr_uid != -1) {
//4
//判断当前进程的有效用户ID是否等于binder_context_mgr_uid
if (binder_context_mgr_uid != current->cred->euid) {
//5
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;//6赋值uid
// 创建Service Manager实体对象binder_node结构体,保存在全局变量binder_context_mgr_node中
binder_context_mgr_node = binder_new_node(proc, NULL, NULL);//7
if (binder_context_mgr_node == NULL) {
ret = -ENOMEM;
goto err;
}
//增加引用计数,引用计数用来管理binder实体对象的生命周期的,使用弱引用和强引用技术管理
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;
...
err_unlocked:
trace_binder_ioctl_done(ret);
return ret;
}
让Service Manager成为上下文管理者在驱动层主要是为其创建一个全局的Service Manager实体对象并保存在binder_context_mgr_node中,注意在调用过程中会通过binder_get_thread方法创建一个binder_thread结构体,用来描述Binder线程池中的一个线程,它的定义如下:
struct binder_thread (
//所属的宿主进程
struct binder_proc *proc;
//一个节点,宿主进程proc使用红黑树来保存其binder线程池中所有的线程,rb_node表示其一个节点
struct rb_node rb_node;
//宿主进程pid
int pid;
//线程状态
int looper;
//binder事务
struct binder_transaction *transaction_stack;
struct list_head todo;
uint32_t return_error; /* Write failed, return error code in read buf */
uint32_t return_error2; /* Write failed, return error code in read */
/* buffer. Used when sending a reply to a dead process that */
/* we are also waiting on */
wait_queue_head_t wait;
struct binder_stats stats;
};
Looper的取值有如下几种:
enum {
BINDER_LOOPER_STATE_REGISTERED = 0x01,
BINDER_LOOPER_STATE_ENTERED = 0x02,
BINDER_LOOPER_STATE_EXITED = 0x04,
BINDER_LOOPER_STATE_INVALID = 0x08,
BINDER_LOOPER_STATE_WAITING = 0x10,
BINDER_LOOPER_STATE_NEED_RETURN = 0x20
};
一个刚创建的binder_thread结构体其状态初始化为BINDER_LOOOPER_STATE_ NEED_RETURN,表示该线程需要马上返回到用户空间。当它可以处理进程间通信请求的时候,Binder驱动程序会将他状态设置为BINDER_LOOPER_STATE_ REGISTERED或者BINDER_LOOOPER_STATE_ENTERED,当线程空闲的时候会将其状态设置为BINDER_LOOPER_STATE_WAITING,当Binder线程退出的时候会将其状态设置为BINDER_LOOPER_STATE_EXITED。
当一个来自Client进程的请求指定要由某一个Binder线程来处理时,这个请求就会加入到相应的binder, thmad结构体的成员变量tedo所表示的狀列中,并且会唤醒这个线程来处理,因为这时候这个线程可能 处于睡眠状态。
当Binder驱动程序决金将二个事务交给一个Binder线程处理时,它就会将该事务封装成一个hinder, transaction结构体,并且将它添加到由线程结构体binder_ thread的成员变量transaction_stack所描述的一 个事务堆栈中。
回到之前Service Manager注册成为上下文管理者,之后将会循环等待Client进程请求了。
2.1.3 循环等待Client进程请求
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
uint32_t readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(uint32_t));//1
for (;;) {
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//2
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);//3
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;
}
}
}
注释1处将BC_ENTER_LOOPER指令通过binder_write函数写入到Binder驱动中,这样当前线程(ServiceManager的主线程)就成为了一个Binder线程,这样就可以处理进程间的请求了。在无限循环中不断的调用注释2处的ioctl函数,它不断的使用BINDER_WRITE_READ指令查询Binder驱动中是否有新的请求,如果有就交给注释3处的binder_parse函数处理。如果没有,当前线程就会在Binder驱动中睡眠,等待新的进程间请求。
frameworks/native/cmds/servicemanager/binder.c
int binder_write(struct binder_state *bs, void *data, size_t len)
{
struct binder_write_read bwr;//1
int res;
bwr.write_size = len;
bwr.write_consumed = 0;
bwr.write_buffer = (uintptr_t) data;//2
bwr.read_size = 0;
bwr.read_consumed = 0;
bwr.read_buffer = 0;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//3
if (res < 0) {
fprintf(stderr,"binder_write: ioctl failed (%s)\n",
strerror(errno));
}
return res;
}
注释1处定义binder_write_read结构体,接下来的代码对bwr进行赋值,其中需要注意的是,注释2处的data的值为BC_ENTER_LOOPER。注释3处的ioctl函数将会bwr中的数据发送给binder驱动,我们已经知道了ioctl函数在Kernel Binder中对应的函数为binder_ioctl,此前分析过这个函数,这里截取BINDER_WRITE_READ命令处理部分
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
...
void __user *ubuf = (void __user *)arg;
...
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))) {
//1
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) {
//2
ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);//3
trace_binder_write_done(ret);
if (ret < 0) {
bwr.read_consumed = 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))) {
//4
ret = -EFAULT;
goto err;
}
break;
}
...
return ret;
}
注释1处的copy_from_user函数,它用于将把用户空间数据ubuf拷贝出来保存到内核数据bwr(binder_write_read结构体)中。注释2处,bwr的输入缓存区有数据时,会调用注释3处的binder_thread_write函数来处理BC_ENTER_LOOPER协议,其内部会将目标线程的状态设置为BINDER_LOOPER_STATE_ENTERED,这样目标线程就是一个Binder线程。
注释4处通过copy_to_user函数将内核空间数据bwr拷贝到用户空间。
在前面提到的无限循环中,不断的使用BINDER_WRITE_READ指令查询Binder驱动中是否有新的请求,所传递的binder_write_read结构体输入缓冲区大小为0,因此驱动程序只会调用binder_thread_read检查Service Manager进程是否有新的进程间通信请求需要处理,
接下来分析下binder驱动的binder_thread_read方法
kernel/goldfish/drivers/staging/android/binder.c
Android Binder机制跨进程通讯流程解析

本文介绍了Android中Binder跨进程通讯机制。它是Android系统跨进程通讯的高效方式,采用CS架构。文中详细分析了Service Manager服务的启动流程、服务注册与获取流程,以及系统服务代理的获取和调用方法,涵盖Java层和native层的Binder实现。
最低0.47元/天 解锁文章
5383

被折叠的 条评论
为什么被折叠?



