Binder - 内核驱动层源码浅析

binder驱动.png

在线内核层代码:http://androidxref.com/kernel_3.18/xref/

1.binder_init

驱动启动时会先调用驱动的binder_init():主要负责注册misc设备,通过调用misc_register来实现

//资源路径: /drivers/staging/android/binder.c
static int __init binder_init(void)
{
  int ret;
  //创建名为binder的单线程工作队列
  binder_deferred_workqueue = create_singlethread_workqueue("binder");
  //...
  //注册misc设备
  ret = misc_register(&binder_miscdev);
  return ret
}
static struct miscdevice binder_miscdev = {
  //次设备号 动态分配
  .minor = MISC_DYNAMIC_MINOR,
  //设备名 binder
  .name = "binder",
  //设备的文件操作结构,file_operations结构
  .fops = &binder_fops
};
//Native层调用驱动层需要通过系统调用(syscall),下面是Native层与驱动层的映射关系
static const struct file_operations binder_fops = {
  .owner = THIS_MODULE,
  .poll = binder_poll,
  .unlocked_ioctl = binder_ioctl,
  .compat_ioctl = binder_ioctl,
  //如Native层调用mmap会调到驱动层的binder_mmap
  .mmap = binder_mmap,
  .open = binder_open,
  .flush = binder_flush,
  .release = binder_release,
};
2.binder_open

打开驱动会调用binder_open():创建binder_proc对象,并把当前进程等信息保存到binder_proc对象,再将binder_proc对象保存到文件指针filp,以及把binder_proc添加到全局链表binder_procs中。

//资源路径:/drivers/staging/android/binder.c
static int binder_open(struct inode *nodp, struct file *filp)
{
  //当前binder进程结构体
  struct binder_proc *proc;
  //为binder_proc结构体在内核申请内存空间
  proc = kzalloc(sizeof(*proc), GFP_KERNEL);
  //current代表当前线程
  get_task_struct(current);
  //将当前线程的task保存到binder进程的tsk
  proc->tsk = current;
  //初始化todo列表
  INIT_LIST_HEAD(&proc->todo);
  //初始化wait队列
  init_waitqueue_head(&proc->wait);
  //将当前进程的nice值转换为进程优先级
  proc->default_priority = task_nice(current);
  
  //同步锁,因为binder支持多线程访问
  binder_lock(__func__);

  //binder_proc对象创建数加1
  binder_stats_created(BINDER_STAT_PROC);
  //将pro_node节点添加到binder_procs的队列头部
  hlist_add_head(&proc->proc_node, &binder_procs);
  //记录当前进程的pid
  proc->pid = current->group_leader->pid;
  //初始化已分发的死亡通知列表
  INIT_LIST_HEAD(&proc->delivered_death);
  //将binder_proc存放在filp的private_data域,以便在之后的mmap、ioctl中获取
  filp->private_data = proc;

  //释放同步锁
  binder_unlock(__func__);
  
  return 0;
}
3.binder_mmap

1.通过用户空间的虚拟内存大小,分配一块内核的虚拟内存
2.分配一块物理内存(4KB)
3.把这块物理内存分别映射到用户空间的虚拟内存和内核的虚拟内存

//资源路径:/drivers/staging/android/binder.c
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
  int ret;
  //内核的虚拟内存
  struct vm_struct *area;
  //从filp中取binder_proc(binder_open方法中保存的)
  struct binder_proc *proc = filp->private_data;
    //保证映射内存大小不超过4M
  if ((vma->vm_end - vma->vm_start) > SZ_4M)
    vma->vm_end = vma->vm_start + SZ_4M;
  
  //同步锁,保证一次只有一个进程分配内存
  mutex_lock(&binder_mmap_lock);
  //判断是否映射过
  if (proc->buffer) {       
    ret = -EBUSY;
    failure_string = "already mapped";
    goto err_already_mapped;
  }
    //采用VM_IOREMAP方式,分配一个连续的内核虚拟内存,与进程虚拟内存大小一致
  area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
  //判断是否分配成功
  if (area == NULL) {
    ret = -ENOMEM;
    failure_string = "get_vm_area";
    goto err_get_vm_area_failed;
  }
  //将proc中的buffer指针指向这块内核的虚拟内存
  proc->buffer = area->addr;
  //计算用户空间和内核空间的地址偏移量
  proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
    //释放同步锁
  mutex_unlock(&binder_mmap_lock);
  //分配物理页的指针数组,大小为vma的等效page个数
  proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
  
  //分配物理页面,同时映射到内核空间和进程空间,先分配1个物理页(4KB),真正数据传输的时候再添加,免得内存浪费。
  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;
  }
  //binder_buffer对象指向proc的buffer地址 
  buffer = proc->buffer;
  //创建进程的buffers链表头
  INIT_LIST_HEAD(&proc->buffers);
  //将binder_buffer地址加入所属进程的buffers队列
  list_add(&buffer->entry, &proc->buffers);
  //上面通过binder_update_page_range已经做了映射,此内存可用
  buffer->free = 1;
  //将空闲的buffer放入proc->free_buffers链表中
  binder_insert_free_buffer(proc, buffer);
  //异步传输的可用空闲空间大小为buffer总大小的一半
  proc->free_async_space = proc->buffer_size / 2;
  //...
  return 0;
}


static int binder_update_page_range(struct binder_proc *proc, int                       allocate,void *start, void *end,struct vm_area_struct *vma)
{
  //上面allocate为1,代表分配内存。如果是1表示释放内存
  if (allocate == 0)
        goto free_range;
  for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
        int ret;
    //①分配一个page的物理内存
    *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
    //②内核空间的虚拟内存映射到物理空间
    ret = map_vm_area(&tmp_area, PAGE_KERNEL, page);
    //用户空间地址=内核空间地址+偏移量
    user_page_addr =(uintptr_t)page_addr + proc->user_buffer_offset;
    //③用户空间的虚拟内存映射到物理内存
    ret = vm_insert_page(vma, user_page_addr, page[0]);
  }
}
4.binder_ioctl

binder_ioctl承载了Binder数据传输部分的主要业务,有两个核心方法 binder_thread_write 和 binder_thread_read

//资源路径:/drivers/staging/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
  //进入休眠状态,直到中断唤醒
  ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
  binder_lock(__func__);
  //获取binder_thread
  thread = binder_get_thread(proc);
  //
  switch (cmd) {
    //binder读写操作
    case BINDER_WRITE_READ:
      ret = binder_ioctl_write_read(filp, cmd, arg, thread);
      if (ret)
        goto err;
      break;
      //...
  }
}


static int binder_ioctl_write_read(struct file *filp,
                                   unsigned int cmd, unsigned long arg,
                                   struct binder_thread *thread)
{
  int ret=0
  //把用户空间数据ubuf拷贝到bwr
  if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
    ret = -EFAULT;
    goto out;
  }
  //当写缓存中有数据,则执行binder写操作
  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;
    }
  }
  //当读缓存中有数据,则执行binder读操作
  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);
    if (!list_empty(&proc->todo))
      //唤醒等待状态的线程
      wake_up_interruptible(&proc->wait);
    //读失败,再将bwr数据写回用户空间,并返回
    if (ret < 0) {
      if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
        ret = -EFAULT;
      goto out;
    }
  }
  //将内核数据bwr拷贝到用户空间ubuf
  if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
    ret = -EFAULT;
    goto out;
  }
  out:
  return ret;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容