1.第一部分 Binder之一:Binder Driver概览:《android框架揭秘读书笔记》
Binder Driver的分析
1.从进程的角度看服务的使用
客户端使用服务端要经历的三个阶段
- 服务注册(服务端与Context Manager之间的IPC)
- 服务检索(客户度与Context Manager之间的IPC)
- 服务使用(客户端与服务端之间的IPC)
1.1 服务注册
服务注册:把服务注册到Context Manager的服务目录中
(1)-(3)servicemanager调用open函数,打开binder driver,然后调用mmap函数,在内核空间开辟一块用于接收ipc数据的buffer,再调用ioctl函数进入待机状态。
(4)-(5)为了注册服务,服务端先打开binder driver,而后调用mmap函数,确定一块buffer,用于接收ipc应答数据。
(6)服务端生成ipc数据,ipc数据包含rpc数据(保存要注册的服务名称),prc代码(servicemanager的注册函数ADD_SERVICE),Handle(servicemanager的Handle值为0)三个部分
(7)服务端调用ioctl函数向binder driver传递IPC数据,Binder Driver将数据再传递给Service Manager
(8)-(9)Service Manager分析IPC数据中的RPC代码,并根据RPC代码调用相应的服务注册函数,而后使用RPC数据中的服务名称,将制定的服务注册到目录中。
(10)服务注册完成后,Service Manager会生成应答数据,并传递给服务端,告知服务已经注册。
1.2 服务检索
服务检索:客户端使用服务端的服务时,需要向ServiceManager请求服务的编号,这个过程叫服务检索。
在该阶段,客户端会调用ServiceManager的服务检索函数,客户端首先生成调用ServiceManager的IPC数据,然后传递给Binder Driver,然后Binder Driver将IPC数据传递给ServiceManager,ServiceManager接收到IPC数据中的RPC代码调用相应的函数。
1:ServiceManager处于待机状态,等待接收IPC数据
2-4:为了向ServiceManager传递IPC数据,客户端打开BinderDriver,而后调用mmap函数准备一块buffer,用于接收IPC应答数据,客户端生成IPC数据,IPC数据包括RPC数据(保存要使用的服务名称),RPC代码(ServiceManger的服务检索函数GET_SERVICE),Handle(ServiceManager的handle为0)三部分。
5:服务端调用ioctl函数向BinderDriver传递数据,BinderDriver根据IPC数据中的Handle,将IPC数据传递给ServiceManager
6:-8:ServiceManager分析IPC数据,根据RPC代码调用服务检索函数,根据RPC数据中保存的服务名称在服务目录中检索服务,查找到服务BInder节点编号,ServiceManager将查找到的Binder节点编号插入到IPC应答数据中。
9:客户端接收IPC应答数据,获得指定服务的Binder节点编号。
1.3 服务使用
1.服务端进入待机状态,等待接收IPC数据,并且当处理完接收的IPC数据之后,它将再次进入待机状态,等待接收新的数据
2-3:为了使用服务,客户端首先生成IPC数据,IPC数据有三个部分组成,包括RPC代码(指定服务函数),RPC数据(服务函数的参数),Handle(指定哪个服务);然后调用ioctl函数,将IPC数据传递给BinderDriver,最后BinderDriver根据IPC数据中的Handle把IPC数据传递给服务端
4-5:服务端分析IPC数据中的RPC代码,根据RPC代码调用服务函数,IPC数据中的RPC数据是函数参数,在函数的内部使用,当指定的函数执行完毕之后,服务端会生成IPC应答数据,其中包括Binder节点编号,而后将IPC应答数据发送给客户端,告知指定的服务函数已经执行完成。
6:客户端接收IPC数据,并进行处理
2.从BinderDriver角度看服务的使用:BinderDriver的运行过程分析
2.1服务注册
ServiceManager先于其他所有服务运行,它最先使用BinderDriver,进入待机状态,等待服务的注册和客户端对服务的检索请求。
1-3:描述的是ServiceManager进入待机状态前使用BinderDriver的过程;binder_proc是一个结构体,这个结构体里包含了一系列指针,以便访问其他结构体,每个进程都有一个binder_proc结构体,通过这个结构体,可以查找到另一个进程,也可以查找到进程接收IPC数据的buffer
1:ServiceManager通过调用open函数调用binder_open函数,BinderDriver使用binder_open函数诶ServiceManager生成并初始化binder_proc
2.ServiceManager在内核开辟一块buffer,用于接收IPC数据:ServiceManger通过调用mmap函数调用BinderDriver的binder_mmap函数,binder_mmap函数在内核空间中分配一块用于接收IPC数据的buffer,并保存到binder_buffer结构体,而binder_buffer结构体会被注册到binder_proc结构体里
3.ServiceManger通过调用ioctl函数调用BinderDriver的binder_ioctl函数,在binder_ioctl中,ServiceManger将处于待机状态,知道另一个进程向其发送IPC数据。
4.服务端通过open调用BinderDriver的binder_open函数,BinderDriver为服务生成binder_proc结构体,并将其初始化。
5.服务端通过mmap函数调用BinderDriver的binder_mmap函数开辟一块空间用于接收IPC应答数据的buffer,并将其保存到binder_buffer结构体,而后服务端生成IPC数据,用来调用ServiceManager的服务注册函数。
6.为了注册服务,服务端将IPC数据传递给BinderDriver;BinderDriver通过IPC数据中的Handle查找到ServiceManager的binde_node和binder_proc
7.为注册的服务生成binder_node结构体,BinderDriver将binder_node分别注册到服务的binder_proc和ServiceManager的binder_proc中。
8.BinderDriver将在第六步中查找到的ServiceManager的binder_proc结构体中查找注册在binder_buffer中的buffer,并传递IPC数据。
9.BinderDriver会记下IPC数据发送进程也就是服务进程的binder_proc结构体,以便在ServiceManager向服务进程发送应答数据时查找服务的binder_proc。
10.BinderDriver让服务处于待机状态,并将ServiceManager从待机状态唤醒,传递IPC数据,ServiceManger从BinderDriver中接收来自服务的IPC数据,ServiceManger根据接收到的数据注册指定的服务,而后生成IPC应答数据,并将其传递给BinderDriver。
11.BinderDriver会在服务的binder_proc结构体中查找binder_buffer里的buffer,并传递IPC应答数据,唤醒服务端,服务端被唤醒后接收IPC数据,并根据IPC数据做相应处理。
2.2 服务检索
1.ServiceManager进入待机状态,等待接收IPC数据
2-3客户端生成并初始化binder_proc结构体,然后开辟一块buffer用于接收IPC数据
4.客户端通过ioctl函数调用BinderDriver的binder_ioctl函数,BinderDriver会查找Servicemanager的binder_proc结构体,这里不生成binder_node结构体,只是将IPC数据拷贝到ServiceManager的接收buffer中,并记下客户端的binder_proc结构体,以便查找IPC应答数据的接收端。
5.BinderDriver让客户端处于待机状态,并唤醒处于等待状态的ServiceManger,而后接收IPC数据,ServiceManager在服务目录中查找请求的服务,把服务的编号插入IPC数据中,并将IPC数据传递给BinderDriver。
6.BinderDriver根据接收到的服务编号查找相应的binder_node结构体,而后将查找到的binder_node结构体注册到客户端的binder_proc中,binder_node结构体用于在服务使用阶段查找服务端的binder_proc结构体。
7.BinderDriver将在第六步中注册的binder_node结构体的编号插入到IPC应答数据中,传递给客户端,唤醒客户端,在使用服务时,客户端会将编号作为Handle使用。
2.3服务使用
1.服务端处于待机状态,用于接收IPC数据
2.客户端把从服务检索阶段获取到的Binder节点编号保存到IPC数据的Handle中,生成IPC数据后传递给BinderDriver,BinderDriver根据IPC数据中的Handle查找相应的binder_node结构体,并根据此查找到注册有binder_node结构体的服务进程的binder_proc结构体,然后通过注册在binder_proc结构体中的binder_buffer结构体,将IPC数据拷贝的buffer中
3.BinderDriver让客户端进入待机状态,唤醒处于待机状态的服务端,服务端接收IPC数据,并通过IPC数据中的RPC代码RPC数据调用相应的服务函数
4.服务函数执行完毕后,服务端会生成IPC应答数据,并将其传递给BinderDriver,然后BinderDriver将数据传递给客户端
3.BinderDriver的函数分析
3.1 binder_open函数
binder_open函数为打开BinderDriver的进程生成并初始化结构体binder_proc;并且初始化等待队列(用来将进程切换到待机状态下)和todo队列(用来在进程被唤醒后执行其中的任务)
3.1.1 binder_proc结构体
binder_proc结构体用来管理BinderIPC所需要的各种信息,包括打开BinderDriver的进程信息,接收IPC数据的buffer信息等,此外,binder_proc拥有其他结构体的指针,为访问其他结构体提供了方便,被称为BinderDriver的根结构体
3.1.2 binder_open源码分析
static HLIST_HEAD(binder_procs);
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;
binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
current->group_leader->pid, current->pid);
//为binder_proc开辟内存空间,也就是初始化binder_proc
proc = kzalloc(sizeof(*proc), GFP_KERNEL);
if (proc == NULL)
return -ENOMEM;
get_task_struct(current);
proc->tsk = current;//1
INIT_LIST_HEAD(&proc->todo);//2
init_waitqueue_head(&proc->wait);//2
proc->default_priority = task_nice(current);
mutex_lock(&binder_lock);
binder_stats_created(BINDER_STAT_PROC);
//把当前生成的proc加入到binder_procs中
hlist_add_head(&proc->proc_node, &binder_procs);
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
filp->private_data = proc;//3
mutex_unlock(&binder_lock);
if (binder_debugfs_dir_entry_proc) {//4
char strbuf[11];
snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);
proc->debugfs_entry = debugfs_create_file(strbuf, S_IRUGO,
binder_debugfs_dir_entry_proc, proc, &binder_proc_fops);
}
return 0;
}
1.task_struct结构体中包含打开BinderDriver的进程的信息,proc->tsk = current将task_struct结构体变量current注册到binder_proc结构体变量的tsk成员中,该变量用来输出当前正在进行BinderIPC的进程。
2.BinderDriver接收到IPC数据后,将要执行的任务保存到proc->todo列表中,第二条语句初始化待机队列,以便将打开的BinderDriver进程切换到待机状态。
3.将proc放到flip中,以便在后面使用,后面使用时只需要用proc=flip->private_data就可以获取到binder_proc
4.在proc/binder/proc目录下生成文件,以便初始化binder_proc结构体或显示与BInderIPC相关的信息,在Android启动后,查看该目录和目录下的文件,可以查看到BinderIPC的进程信息。
3.2 binder_mmap函数
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
struct vm_struct *area;
//1:
struct binder_proc *proc = filp->private_data;
const char *failure_string;
struct binder_buffer *buffer;
if ((vma->vm_end - vma->vm_start) > SZ_4M)
vma->vm_end = vma->vm_start + SZ_4M;
binder_debug(BINDER_DEBUG_OPEN_CLOSE,
"binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
proc->pid, vma->vm_start, vma->vm_end,
(vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
(unsigned long)pgprot_val(vma->vm_page_prot));
if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
ret = -EPERM;
failure_string = "bad vm_flags";
goto err_bad_arg;
}
vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
if (proc->buffer) {
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);//2
if (area == NULL) {
ret = -ENOMEM;
failure_string = "get_vm_area";
goto err_get_vm_area_failed;
}
proc->buffer = area->addr; //3
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;//4
#ifdef CONFIG_CPU_CACHE_VIPT
if (cache_is_vipt_aliasing()) {
while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
vma->vm_start += PAGE_SIZE;
}
}
#endif
proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
if (proc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
proc->buffer_size = vma->vm_end - vma->vm_start;
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
//5
if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
ret = -ENOMEM;
failure_string = "alloc small buf";
goto err_alloc_small_buf_failed;
}
buffer = proc->buffer;//6
INIT_LIST_HEAD(&proc->buffers);
list_add(&buffer->entry, &proc->buffers);
buffer->free = 1;
binder_insert_free_buffer(proc, buffer);//7
proc->free_async_space = proc->buffer_size / 2;
barrier();
proc->files = get_files_struct(current);
proc->vma = vma;
/*printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p\n",
proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
return 0;
err_alloc_small_buf_failed:
kfree(proc->pages);
proc->pages = NULL;
err_alloc_pages_failed:
vfree(proc->buffer);
proc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
err_bad_arg:
printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n",
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
}
1.binder_mmap函数的第一个参数是file结构体指针,它指向BinderDriver的文件描述符,file结构体指针的private_data成员变量是当前进程的binder_proc结构体,在binder_open中已经被赋值;binder_mmap第二个参数是用户空间的buffer信息,由mmap函数与内核空间映射在一起。
2.get_vm_area是一个内核函数,通过该函数可以向系统申请一块可用的虚拟内存空间,也就是在内核中申请并保留一块连续的内核虚拟内存区域,若内核的VMALLOC区域有一块符合指定尺寸的空间,则生成一个vm_struct结构体,在把空间的起始地址付给vm_struct结构体的addr变量后返回
3.调用get_vm_area函数将返回vm_struct结构体,并将其赋值给area变量,此时area->addr中存放着内核空间的起始地址,使用proc->buffer=area->addr后,proc->buffer其实存放了内核空间中接收IPC数据的Buffer的起始地址。
4.这行代码用来计算用户空间buffer地址与内核空间buffer地址的偏移。proc->user_buffer_offset是int类型变量,是一个负值,偏移信息用来告诉进程用户空间的buffer地址,在BinderDriver接收IPC数据后,用户空间的Buffer与保存IPC数据的内核空间映射在一起。
5.调用binder_update_page_range函数,分配物理页,并将存在于物理内存与虚拟内存的内核空间的接收Buffer与用户空间的接收Buffer映射起来
6.该语句用来将IPC数据保存到binder_buffer中
7.binder_insert_free_buffer函数,将bindeer_buffer结构体注册到当前进程的binder_proc结构体的free_buffers中,free_buffers变量是指接收IPC数据的buffer,根据IPC数据的大小,分配不同的free_buffers,以便IPC接收IPC数据
3.3 binder_ioctl函数
3.3.1 binder_write_read结构体
struct binder_write_read {
//用于发送数据
signed long write_size; /* bytes to write */
signed long write_consumed; /* bytes consumed by driver */
unsigned long write_buffer;
//用于接收数据
signed long read_size; /* bytes to read */
signed long read_consumed; /* bytes consumed by driver */
unsigned long read_buffer;
};
3.3.2 binder_transaction_data结构体(Handle,RPC代码,RPC数据保存在该结构体中)
以发送IPC数据进程的buffer来分析
binder_write_read结构体中包含着用户空间生成的IPC数据,BinderDriver也拥有一个相同的结构体,用户空间设置完binder_write_read结构体数据后,调用ioctl函数传递给BInderDrier,BinderDrier调用copy_from_user函数将用户空间中的数据拷贝到自身的binder_write_read结构体中,相反,这IPC应答阶段,BinderDrier将调用copy_to_user函数,将自身的binder_write_read结构体中的数据拷贝到用户空间。
struct binder_transaction_data {
/* The first two are only used for bcTRANSACTION and brTRANSACTION,
* identifying the target and contents of the transaction.
*/
union {
size_t handle; /* target descriptor of command transaction */
void *ptr; /* target descriptor of return transaction */
} target;
void *cookie; /* target object cookie */
unsigned int code; /* transaction command */
/* General information about the transaction. */
unsigned int flags;
pid_t sender_pid;
uid_t sender_euid;
size_t data_size; /* number of bytes of data */
size_t offsets_size; /* number of bytes of offsets */
/* If this transaction is inline, the data immediately
* follows here; otherwise, it ends with a pointer to
* the data buffer.
*/
union {
struct {
/* transaction data */
const void *buffer;
/* offsets from buffer to flat_binder_object structs */
const void *offsets;
} ptr;
uint8_t buf[8];
} data;
};
3.3.3 概览
Binder架构
http://gityuan.com/images/binder/java_binder/java_binder.jpg
Binder进程和线程
http://gityuan.com/images/binder/summary/binder_proc_relation.png
参考
[Binder系列总结](http://gityuan.com/2015/11/28/binder-summary/)
《android框架揭秘》