Binder驱动

Binder是Android系统提供一种IPC机制,Binder驱动也是一个标准的Linux驱动。Binder Driver被注册成一个misc device,并向上层提供一个/dev/binder节点,Binder节点并不是对应真实的硬件设备。Binder驱动运行于内核态,可提供open、ioctl、mmap等常用的文件操作。

Android系统基本上可以看作是一个基于Binder通信的C/S架构,Binder主要实现以下功能:

  • 用驱动程序来推进进程间的通信
  • 通过共享内存来提高性能
  • 为进程请求分配每个进程的线程池
  • 针对系统中的对象引入引用计数和跨进程的对象引用映射
  • 进程间同步调用

Binder驱动源码在Kernel工程中,以常规的misc设备来分析源码:

misc“杂项”设备统一主设备号为10。

/* \linux-3.5\drivers\staging\android\binder.c*/

Binder驱动注册相关的代码:

static struct miscdevice binder_miscdev = {àmisc设备结构体
      .minor = MISC_DYNAMIC_MINOR,à动态分配次设备号
      .name = "binder",à驱动名称
      .fops = &binder_fopsàBinder驱动支持的文件操作
};
 
static int __init binder_init(void)
{
      int ret;
 
      binder_deferred_workqueue = create_singlethread_workqueue("binder");
      if (!binder_deferred_workqueue)
           return -ENOMEM;
 
      binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
      if (binder_debugfs_dir_entry_root)
           binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
                                   binder_debugfs_dir_entry_root);
      ret = misc_register(&binder_miscdev);à注册misc设备
      if (binder_debugfs_dir_entry_root) {
           debugfs_create_file("state",
                          S_IRUGO,
                          binder_debugfs_dir_entry_root,
                          NULL,
                          &binder_state_fops);
           debugfs_create_file("stats",
                          S_IRUGO,
                          binder_debugfs_dir_entry_root,
                          NULL,
                          &binder_stats_fops);
           debugfs_create_file("transactions",
                          S_IRUGO,
                          binder_debugfs_dir_entry_root,
                          NULL,
                          &binder_transactions_fops);
           debugfs_create_file("transaction_log",
                          S_IRUGO,
                          binder_debugfs_dir_entry_root,
                          &binder_transaction_log,
                          &binder_transaction_log_fops);
           debugfs_create_file("failed_transaction_log",
                          S_IRUGO,
                          binder_debugfs_dir_entry_root,
                          &binder_transaction_log_failed,
                          &binder_transaction_log_fops);
      }
      return ret;
}
 
device_initcall(binder_init);à设备驱动接口函数,在linux misc设备中,使用module_init和module_exit是为了同时兼容支持静态编译驱动和动态编译驱动,而Binder使用device_initcall的目的就是不让Binder驱动支持动态编译,而且需要在内核做镜像

Binder驱动填写的file_operations结构体:

/* \linux-3.5\drivers\staging\android\binder.c*/
static const struct file_operations binder_fops = {
      .owner = THIS_MODULE,
      .poll = binder_poll,
      .unlocked_ioctl = binder_ioctl,
      .mmap = binder_mmap,
      .open = binder_open,
      .flush = binder_flush,
      .release = binder_release,
};

其中使用最多的是binder_ioctl、binder_mmap和binder_open,而一般文件操作需要的read()和write()则没有出现,是因为它们的功能完全可以用ioctl和mmap来替代,后者更加灵活。

上层进程在访问Binder驱动时,首先需要打开/dev/binder节点,这个操作是在binder_open()中完成的,代码如下:

/* \linux-3.5\drivers\staging\android\binder.c*/
static int binder_open(struct inode *nodp, struct file *filp)
{
      struct binder_proc *proc;àBinder驱动会在/proc系统目录下生成各种管理信息(如/proc/binder/proc, /proc/binder/state, /proc/binder/stats等)
 
      binder_debug(BINDER_DEBUG_OPEN_CLOSE, "binder_open: %d:%d\n",
                current->group_leader->pid, current->pid);
 
      proc = kzalloc(sizeof(*proc), GFP_KERNEL);à分配空间
      if (proc == NULL)
           return -ENOMEM;
      get_task_struct(current);
      proc->tsk = current;
      INIT_LIST_HEAD(&proc->todo);àtodo链表
      init_waitqueue_head(&proc->wait);àwait链表
      proc->default_priority = task_nice(current);à设置默认优先级
 
      binder_lock(__func__);à获取锁
 
      binder_stats_created(BINDER_STAT_PROC);àbinder_stats是binder中的统计数据载体
      hlist_add_head(&proc->proc_node, &binder_procs);à将proc加入到全局binder_procs的哈希表中,这样任何一个进程都可以访问其他进程的binder_proc对象
      proc->pid = current->group_leader->pid;à进程pid
      INIT_LIST_HEAD(&proc->delivered_death);
      filp->private_data = proc;à将这个proc与filp关联起来,下次能通过filp找到这个proc
 
      binder_unlock(__func__);à解锁
 
      if (binder_debugfs_dir_entry_proc) {à在binder_proc目录中创建只读文件/proc/binder/proc/$pid,功能是输出当前binder proc对象。文件以pid命名,但该pid字段并不是当前进程、线程的id,而是线程组的pid,表示线程组中第一个线程的pid(proc->pid = current->group_leader->pid赋值给pid)
           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;
}

 

mmap()函数可以把设备指定的内存块直接映射到应用程序的内存空间中,而Binder就是通过binder_mmap函数在进程间共享内存的。代码如下:

/* \linux-3.5\drivers\staging\android\binder.c*/
static int binder_mmap(struct file *filp, struct vm_area_struct *vma) à描述一块应用程序使用的虚拟内存,内核专门维护了vma的链表和树状结构,vma的功能是管理进程地址空间中不同区域的数据结构
{
      int ret;
      struct vm_struct *area;àarea变量是Binder驱动中对虚拟内存的描述
      struct binder_proc *proc = filp->private_data;àproc是Binder驱动为应用进程分配的一个数据结构,用于存储和该进程有关的所有信息。这里取出进程对应的proc对象。
      const char *failure_string;
      struct binder_buffer *buffer;
 
      if ((vma->vm_end - vma->vm_start) > SZ_4M)à当应用程序申请的内存超过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) {à判断是否禁止了mmap
           ret = -EPERM;
           failure_string = "bad vm_flags";
           goto err_bad_arg;
      }
      vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
à加入写标识
      mutex_lock(&binder_mmap_lock);à加锁
      if (proc->buffer) {à判断是否做过映射,每个进程线程只能做一次映射,proc->buffer用来存储最终的mmap结果,所以不为空则表示做过映射
           ret = -EBUSY;
           failure_string = "already mapped";
           goto err_already_mapped;
      }
 
      area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);à get_vm_area函数用于为binder驱动获取一段可用的虚拟内存空间
      if (area == NULL) {
           ret = -ENOMEM;
           failure_string = "get_vm_area";
           goto err_get_vm_area_failed;
      }
      proc->buffer = area->addr;à将proc中的buffer指针指向虚拟内存的起始地址
      proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;à计算它和应用程序中相关联的虚拟内存地址(vma->vm_start)的偏移量
      mutex_unlock(&binder_mmap_lock);
 
#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;
 
      if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {à binder_update_page_range真正开始申请物理页面static int binder_update_page_range(struct binder_proc *proc, int allocate,
                          void *start, void *end,
                          struct vm_area_struct *vma)
• proc: 申请内存的进程所持有的bind_proc对象
• allocate:是申请还是释放。1申请0释放
• start: Binder中虚拟内存起点(页表映射是从虚拟内存到物理内存)
• end: 虚拟内存终点
• vma:应用程序中虚拟内存段的描述
           ret = -ENOMEM;
           failure_string = "alloc small buf";
           goto err_alloc_small_buf_failed;
      }
      buffer = proc->buffer;
      INIT_LIST_HEAD(&proc->buffers);
      list_add(&buffer->entry, &proc->buffers);
      buffer->free = 1;
      binder_insert_free_buffer(proc, buffer);à通过此函数能将一个空闲内核缓冲区加入进程中的空闲内核缓冲区的红黑树中
      proc->free_async_space = proc->buffer_size / 2;
      barrier();
      proc->files = get_files_struct(proc->tsk);
      proc->vma = vma;
      proc->vma_vm_mm = vma->vm_mm;
 
      /*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:
      mutex_lock(&binder_mmap_lock);
      vfree(proc->buffer);
      proc->buffer = NULL;
err_get_vm_area_failed:
err_already_mapped:
      mutex_unlock(&binder_mmap_lock);
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;
}

 

binder_ioctl函数:

/* \linux-3.5\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;
 
      /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, 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__);
      thread = binder_get_thread(proc);
      if (thread == NULL) {
           ret = -ENOMEM;
           goto err;
      }
 
      switch (cmd) {
      case BINDER_WRITE_READ: {à读写操作,可以用此向binder中读写数据
           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) {
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;
      }
      case BINDER_SET_MAX_THREADS:à设置支持的最大线程数。因为客户端可以并发向服务器端发送请求,如果Binder驱动发现当前的线程数量已经超过设置值,则会告知binder server停止启动新的线程
           if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
                 ret = -EINVAL;
                 goto err;
           }
           break;
      case BINDER_SET_CONTEXT_MGR:àServiceManager专用,设置为binder管理
           if (binder_context_mgr_node != NULL) {
                 printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
                 ret = -EBUSY;
                 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;
      case BINDER_THREAD_EXIT:à通知binder线程退出,每个线程在退出时都会告诉binder驱动释放相关资源,否则会有内存泄漏
           binder_debug(BINDER_DEBUG_THREADS, "binder: %d:%d exit\n",
                      proc->pid, thread->pid);
           binder_free_thread(proc, thread);
           thread = NULL;
           break;
      case BINDER_VERSION:à版本号
           if (size != sizeof(struct binder_version)) {
                 ret = -EINVAL;
                 goto err;
           }
           if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, &((struct binder_version *)ubuf)->protocol_version)) {
                 ret = -EINVAL;
                 goto err;
           }
           break;
      default:
           ret = -EINVAL;
           goto err;
      }
      ret = 0;
err:
      if (thread)
           thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
      binder_unlock(__func__);
      wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
      if (ret && ret != -ERESTARTSYS)
           printk(KERN_INFO "binder: %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_thread_write中尚有一系列的子指令,暂略。。。