Binder 解析之驱动层理解(二)

586 阅读5分钟

一、 驱动初始化

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,
};

从上面可以看出,系统调用和驱动函数的对应关系:

image.png

二、 驱动提供的函数

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;
}

只贴出来两个核心的功能:

  1. 收发binder数据
  2. 设置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

进行了两次拷贝。

  1. 从client用户态到内核态
  2. 从内核态到server的用户态

4.4.2 真正的数据

只进行了一次拷贝。