一、 驱动初始化
Binder驱动作为misc 设备
,在系统启动的时候就会完成注册。
common/drivers/char/misc.c
static int __init init_binder_device(const char *name)
{
int ret;
struct binder_device *binder_device;
binder_device = kzalloc(sizeof(*binder_device), GFP_KERNEL);
if (!binder_device)
return -ENOMEM;
binder_device->miscdev.fops = &binder_fops; //这个就是驱动注册函数
...
// 调用misc_register注册binder驱动
ret = misc_register(&binder_device->miscdev);
if (ret < 0) {
kfree(binder_device);
return ret;
}
hlist_add_head(&binder_device->hlist, &binder_devices);
return ret;
}
binder_fops
对应的结构体:
const struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.mmap = binder_mmap,
.open = binder_open,
.flush = binder_flush,
.release = binder_release,
};
从上面可以看出,系统调用和驱动函数的对应关系:
二、 驱动提供的函数
common/drivers/android/binder.c
2.1 binder_open()
static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc, *itr; //每一个进程对应一个binder_proc结构体
struct binder_device *binder_dev;
struct binderfs_info *info;
struct dentry *binder_binderfs_dir_entry_proc = NULL;
bool existing_pid = false;
proc = kzalloc(sizeof(*proc), GFP_KERNEL); // 分配内存空间
if (proc == NULL)
return -ENOMEM;
...
INIT_LIST_HEAD(&proc->todo); //初始化todo 链表
init_waitqueue_head(&proc->freeze_wait); wait链表
/* binderfs stashes devices in i_private */
...
refcount_inc(&binder_dev->ref);
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);
filp->private_data = proc; // file中的private_data指向biner_proc对象
mutex_lock(&binder_procs_lock);
hlist_for_each_entry(itr, &binder_procs, proc_node) {
if (itr->pid == proc->pid) {
existing_pid = true;
break;
}
}
hlist_add_head(&proc->proc_node, &binder_procs); // 把binder_node节点加到binder_proc表头的队列中
mutex_unlock(&binder_procs_lock);
...
return 0;
}
不管是client还是server,还是serviceManager都会先打开驱动。当某个进程打开驱动时候,内核会创建binder_proc对象(其实就是代表了这个进程),把proc加入到静态全局的procs链表中。 proc对象中有binder_node节点链表。
2.2 binder_mmap()
static int binder_mmap(struct file *filp, struct vm_area_struct *vma) //vma 是应用进程的某块虚拟内存区域
{
// 得到进程的proc结构体
struct binder_proc *proc = filp->private_data;
if (proc->tsk != current->group_leader)
return -EINVAL;
if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
proc->pid, vma->vm_start, vma->vm_end, "bad vm_flags", -EPERM);
return -EPERM;
}
vma->vm_flags |= VM_DONTCOPY | VM_MIXEDMAP;
vma->vm_flags &= ~VM_MAYWRITE;
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
return binder_alloc_mmap_handler(&proc->alloc, vma);
}
int binder_alloc_mmap_handler(struct binder_alloc *alloc,
struct vm_area_struct *vma)
{
int ret;
const char *failure_string;
struct binder_buffer *buffer; // binder驱动对应的物理内存的内核中虚拟内存描述
mutex_lock(&binder_alloc_mmap_lock);
if (alloc->buffer_size) { //是否已经映射??
ret = -EBUSY;
failure_string = "already mapped";
goto err_already_mapped;
}
alloc->buffer_size = min_t(unsigned long, vma->vm_end - vma->vm_start,
SZ_4M);// 分配应用的虚拟内存空间,最大值为4M。
mutex_unlock(&binder_alloc_mmap_lock);
alloc->buffer = (void __user *)vma->vm_start; //buffer指向应用进程的虚拟内存的起始地址
alloc->pages = kcalloc(alloc->buffer_size / PAGE_SIZE,
sizeof(alloc->pages[0]),
GFP_KERNEL); //计算偏移量offset。实际上只为进程分配了一个物理页的大小 4k
if (alloc->pages == NULL) {
ret = -ENOMEM;
failure_string = "alloc page array";
goto err_alloc_pages_failed;
}
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
if (!buffer) {
ret = -ENOMEM;
failure_string = "alloc buffer struct";
goto err_alloc_buf_struct_failed;
}
buffer->user_data = alloc->buffer;
list_add(&buffer->entry, &alloc->buffers);
buffer->free = 1;
binder_insert_free_buffer(alloc, buffer);
alloc->free_async_space = alloc->buffer_size / 2;
binder_alloc_set_vma(alloc, vma);
mmgrab(alloc->vma_vm_mm);
return 0;
return ret;
主要职责:
- vma表示应用进程的虚拟地址空间。
- 通过
mmap()
机制让vma
与某块物理页内存
进行了映射。 - 同时让bider驱动内核的虚拟地址空间
binder_proc->buffer
与应用进程的虚拟内存大小一致 - 让该
物理页
与驱动的虚拟内存空间
进行映射。 - 至此,应用进程就和驱动内核映射了同一块物理空间,应用进程可直接进行访问,而不用拷贝。
2.3 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;
/*pr_info("binder_ioctl: %d:%d %x %lx\n",
proc->pid, current->pid, cmd, arg);*/
binder_selftest_alloc(&proc->alloc);
trace_binder_ioctl(cmd, arg);
//挂起应用进程,等待唤醒
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
//从proc的threads链表中查询 thread当前线程
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
switch (cmd) {
case BINDER_WRITE_READ: // 接收和发送binder数据
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
...
case BINDER_SET_CONTEXT_MGR_EXT: { // 设置serviceManager为全局静态的binder大管家
struct flat_binder_object fbo;
if (copy_from_user(&fbo, ubuf, sizeof(fbo))) {
ret = -EINVAL;
goto err;
}
ret = binder_ioctl_set_ctx_mgr(filp, &fbo);
if (ret)
goto err;
break;
}
...
default:
ret = -EINVAL;
goto err;
}
ret = 0;
return ret;
}
只贴出来两个核心的功能:
- 收发binder数据
- 设置serviceManager为全局上下文。
2.3.1 binder_ioctl_write_read()
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;
if (size != sizeof(struct binder_write_read)) {
ret = -EINVAL;
goto out;
}
//把数据从用户空间拷贝到内核空间
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
// write_size>0,表示进程向驱动中写数据
if (bwr.write_size > 0) {
ret = binder_thread_write(proc, thread,
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 out;
}
}
// read_size>0,表示进程从驱动中读数据
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
binder_inner_proc_lock(proc);
if (!binder_worklist_empty_ilocked(&proc->todo))
binder_wakeup_proc_ilocked(proc);
binder_inner_proc_unlock(proc);
if (ret < 0) {
// 读取数据错误,把数据拷贝回用户空间
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
out:
return ret;
}
向驱动写入数据或者读取数据。
2.3.2 binder_ioctl_set_ctx_mgr()
设置 serverManager为binder "大管家"。
三、驱动中涉及的结构体
3.1 binder_node
一个binder_node表示一个服务。加入一个进程有多个服务,那么proc中就会有多个binder_node。 binder_node中的最重要的字段当属ptr、cookies、proc。
3.2 binder_write_read
ioctl(bs->fd,BINDER_WRITE_READ,&bwr)
binder_write_read{
long write_buffer;
long write_size;
long read_buffer;
long read_size;
long read_consumed;
long write_consumed;
...
}
其中 write_buffer/read_buffer 指向了 writebuf结构体:
struct{
uint32_t cmd;
struct binder_transaction_data_txn;
} _attribute_ writebuf
3.3 binder_proc
binder_proc{
rb_root refs_by_desc; //存储了binder_refs链表
rb_root refs_by_node;//存储了binder_refs链表
rb_root nodes; //该进程所以的服务
rb_root threads; //所有的binder线程,红黑树。binder_thread 每个线程处理一个client
void *buffers;
list_head buffers;
list_head todo; //todo 链表
...
}
3.4 binder_ref
获取一个服务对应一个handle值(是一个整数值),类似于打开一个文件对应一个fd值。应用层传递过来的handle值,在内核中对应binder_ref结构体,表示对一个服务的引用。而一个服务是一个binder_node。
binder_ref {
binder_node *node; //对应某个服务
int32_t desc;// 整数值,对应handle值
...
}
binder_node{
binder_proc *proc;
user *ptr; //
user *cookies; //对应c++层的bnxxxService类,用来回调到c++层
}
内核会根据handle值来找到对应的binder_ref,进而找到binder_node服务。在通过binder_node找到binder_proc 目的进程。最后把数据放入todo链表中,唤醒目的进程,开始从内核读取数据。
四、驱动中具体过程总结
4.1 Server端注册服务
- 为服务创建binder_node:cookies指向native层的BnxxxService对象;proc指向server进程。
- 向serviceManager注册该binder_node。在内核中会创建binder_ref,node指向该binder_node,同时分配desc值(由注册的顺序决定..)。
- 回到serviceManager的用户态,在svcinfos服务链表中,加入一个svcinfo对象。
4.2 Client端获取服务
- 从serviceManager中查询name为xxx的服务。 serviceManager会从服务链表svcinfos中找到对应的svcinfo,返回handle给驱动的内核态。
- 驱动从在serviceManager的binder_proc结构体中,根据handle从refs_by_desc找到binder_ref,在从binder_ref中找到binder_node。
- 给client进程的binder_proc中,创建新的binder_ref,其中的node指向前面找到的binder_node,同时分配desc(由获取服务的顺序决定)。
- 驱动程序返回desc给client,也就是用户态的handle。
4.3 Client 向 Server发送数据
- client 构造数据,通过ioctl(handle)发送数据
- client 传入handle,进入内核。根据handle找到binder_ref,从中binder_node,在binder_node找到binder_proc server进程。
- 把数据传入server的todo链表中,唤醒server进程读取数据,client进入休眠。
- server进程被唤醒,从todo中取出数据,返回给用户空间
- server 处理数据
- server 把结果通过ioctl(handle)写回给client(本质上也是放入binder_proc的todo链表),server进入休眠
- client 从todo链表中读取数据,返回给用户空间。
4.4 关于数据的拷贝过程总结
4.4.1 对于binder_write_read
进行了两次拷贝。
- 从client用户态到内核态
- 从内核态到server的用户态
4.4.2 真正的数据
只进行了一次拷贝。