Binder源码分析之驱动层(原)

前言

        在《 Binder源码分析之ServiceManager 》一文中我们介绍了利用Binder进行通讯的流程,即ServiceManager把自己注册为“管理员”之后,负责其他Service的add操作,或者其他Client的get操作。在这个过程中,有一些接口我们并没有深入分析,比如:
         1、打开Binder
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. open("/dev/binder", O_RDWR)  
         2、映射虚拟空间
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0)  

        3、设置ServiceManager为管理员

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0)  
         4、与其他Service或者Client交换数据
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. ioctl(bs->fd, BINDER_WRITE_READ, &bwr)  
        由于这些接口都属于Binder驱动的范畴,因此我们放到本章中来学习。

一、Binder驱动概述


1.1、Binder驱动所处的地位

        在整个Binder框架中,涉及到四个参与方:ServiceManager、Binder驱动、Service、Client。 其中ServiceManager是管理者,Binder是驱动提供者,Service和Client可以看作是Binder的使用方

        在上面提到的四个参与方中,只有Binder驱动运行于内核控件,其他部分全部运行于用户空间


1.2、Binder驱动核心

         Binder驱动的核心是维护一个binder_proc类型的链表。里面记录了包括ServiceManager在内的所有Client信息,当Client去请求得到某个Service时,Binder驱动就去binder_proc中查找相应的Service返回给Client,同时增加当前Service的引用个数

        Binder驱动源码主要在google-code\kernel\drivers\staging\Android\binder.c中,下面我们分别介绍Binder驱动中重要的接口。


二、Binder初始化过程

        Binder驱动的初始化是在binder_init()函数中完成的:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static int __init binder_init(void) {  
  2.     int ret;  
  3.     binder_deferred_workqueue = create_singlethread_workqueue("binder");  
  4.   
  5.     //创建binder设备文件夹  
  6.     binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);  
  7.     if (binder_debugfs_dir_entry_root)  
  8.         binder_debugfs_dir_entry_proc = debugfs_create_dir("proc", binder_debugfs_dir_entry_root);  
  9.   
  10.     //注册成为misc设备  
  11.     ret = misc_register(&binder_miscdev);  
  12.     if (binder_debugfs_dir_entry_root) {  
  13.         //创建各个文件  
  14.         debugfs_create_file("state", S_IRUGO, binder_debugfs_dir_entry_root, NULL, &binder_state_fops);  
  15.         debugfs_create_file("stats", S_IRUGO, binder_debugfs_dir_entry_root, NULL, &binder_stats_fops);  
  16.         debugfs_create_file("transactions", S_IRUGO, binder_debugfs_dir_entry_root, NULL, &binder_transactions_fops);  
  17.         debugfs_create_file("transaction_log", S_IRUGO, binder_debugfs_dir_entry_root, &binder_transaction_log, &binder_transaction_log_fops);  
  18.         debugfs_create_file("failed_transaction_log", S_IRUGO, binder_debugfs_dir_entry_root, &binder_transaction_log_failed, &binder_transaction_log_fops);  
  19.     }  
  20.     return ret;  
  21. }  
        上面的过程 将binder_miscdev注册为misc设备
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static struct miscdevice binder_miscdev = {  
  2.     .minor = MISC_DYNAMIC_MINOR,  
  3.     .name = "binder",  
  4.     .fops = &binder_fops  
  5. };  
        这就是Binder的初始化过程,在这个过程中,把Binder驱动(binder_miscdev)注册为了misc设备,并且创建了一些必要的目录。另外,我们来看binder_miscdev这个数据的fops成员变量,他的作用是标识当前设备的操作函数:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static const struct file_operations binder_fops = {  
  2.     .owner = THIS_MODULE,  
  3.     .poll = binder_poll,  
  4.     .unlocked_ioctl = binder_ioctl,  
  5.     .mmap = binder_mmap,  
  6.     .open = binder_open,  
  7.     .flush = binder_flush,  
  8.     .release = binder_release,  
  9. };  

        从这个成员变量的定义中我们可以看到这个设备所具备的接口,其中最重要的就是binder_ioctl、open和mmap。当我们对这个设备进行各种操作时,就会调用其中的函数。


三、打开Binder的方法

        我们在《 Binder源码分析之ServiceManager 》中介绍过,ServiceManager初始化的第一步就是打开Binder设备:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. bs->fd = open("/dev/binder", O_RDWR);  
        这里的open函数,将会调用到当前设备的open接口,对于Binder设备来说,就是前面看到的binder_fops中的binder_open()函数:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static int binder_open(struct inode *nodp, struct file *filp) {  
  2.     struct binder_proc *proc;  
  3.       
  4.     proc = kzalloc(sizeof(*proc), GFP_KERNEL);  
  5.     if (proc == NULL)  
  6.         return -ENOMEM;  
  7.     //得到当前进程  
  8.     get_task_struct(current);  
  9.     proc->tsk = current;  
  10.     INIT_LIST_HEAD(&proc->todo);  
  11.     init_waitqueue_head(&proc->wait);  
  12.     //记录进程优先级  
  13.     proc->default_priority = task_nice(current);  
  14.     mutex_lock(&binder_lock);  
  15.     binder_stats_created(BINDER_STAT_PROC);  
  16.     hlist_add_head(&proc->proc_node, &binder_procs);  
  17.     proc->pid = current->group_leader->pid;  
  18.     INIT_LIST_HEAD(&proc->delivered_death);  
  19.   
  20.     //把proc保存在Binder描述符的private_data中  
  21.     filp->private_data = proc;  
  22.     mutex_unlock(&binder_lock);  
  23.   
  24.     return 0;  
  25. }  

        我们看到,在binder_open的过程中,构建了binder_procs类型的proc数据,并把他保存在file的private_data中。


四、映射虚拟空间的过程

        我们在ServiceManager的分析中讲到,打开Binder设备之后紧接着就进行了地址映射:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);  
        与open操作类似,这里的mmap操作对应了Binder驱动中binder_fops的mmap,也就是binder_mmap(),我们即将看到,在binder_mmap()的过程中, Binder驱动将会把同一块物理页面分别映射到内核空间和进程空间
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.     static int binder_mmap(struct file *filp, struct vm_area_struct *vma) {  
  2.         int ret;  
  3.         //需要映射的内核空间地址信息  
  4.         struct vm_struct *area;  
  5.         //取出binder_open时保存的binder_proc数据  
  6.         struct binder_proc *proc = filp->private_data;  
  7.         struct binder_buffer *buffer;  
  8.   
  9.         //保证这块内存最多只有4M    
  10.         if ((vma->vm_end - vma->vm_start) > SZ_4M)  
  11.             vma->vm_end = vma->vm_start + SZ_4M;  
  12.   
  13.         vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;  
  14.   
  15.         //申请一段内存空间给内核进程  
  16.         area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);  
  17.   
  18.         //得到映射的内核空间虚拟地址首地址  
  19.         proc->buffer = area->addr;  
  20.         //计算用户空间与映射的内核空间的地址偏移量  
  21.         proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;  
  22.   
  23.         //得到映射地址的页数  
  24.         proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);  
  25.         //映射空间的大小  
  26.         proc->buffer_size = vma->vm_end - vma->vm_start;  
  27.   
  28.         vma->vm_ops = &binder_vm_ops;  
  29.         vma->vm_private_data = proc;  
  30.   
  31.         //为虚拟地址空间proc->buffer ~ proc->buffer + PAGE_SIZE  分配一个空闲的物理页面  
  32.         if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {  
  33.             ret = -ENOMEM;  
  34.             failure_string = "alloc small buf";  
  35.             goto err_alloc_small_buf_failed;  
  36.         }  
  37.         buffer = proc->buffer;  
  38.         INIT_LIST_HEAD(&proc->buffers);  
  39.         list_add(&buffer->entry, &proc->buffers);  
  40.         buffer->free = 1;  
  41.         binder_insert_free_buffer(proc, buffer);  
  42.         proc->free_async_space = proc->buffer_size / 2;  
  43.         barrier();  
  44.         proc->files = get_files_struct(current);  
  45.         proc->vma = vma;  
  46.   
  47.         return 0;  
  48.   
  49. err_alloc_small_buf_failed:  
  50.         kfree(proc->pages);  
  51.         proc->pages = NULL;  
  52. err_alloc_pages_failed:  
  53.         vfree(proc->buffer);  
  54.         proc->buffer = NULL;  
  55. err_get_vm_area_failed:  
  56. err_already_mapped:  
  57. err_bad_arg:  
  58.         return ret;  
  59.     }  
        我们看到,binder_mmap()的参数中有一个vm_area_struct类型的vma变量,同时在binder_mmap()的内部又定义了一个vm_struct类型的area变量,这两个变量分别代表映射的进程空间虚拟地址信息和内核空间映射的虚拟地址信息。
        在binder_mmap()的开始地方,将申请的进程空间地址的大小约束在了4M,然后通过get_vm_area()函数在内核空间映射了一块虚拟地址,同时把进程空间的虚拟地址和内核空间的虚拟地址之间偏移量放入了proc->user_buffer_offset变量中,接着, 调用binder_update_page_range()函数为进程空间和内核空间的虚拟地址映射同一个物理页面,从而达到进程空间和内核空间共享一块物理页面的目的
        我们具体来看一下如何把同一块物理页面同时插入内核空间和用户空间中:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static int binder_update_page_range(struct binder_proc *proc, int allocate, void *start, void *end, struct vm_area_struct *vma) {  
  2.     void *page_addr;  
  3.     unsigned long user_page_addr;  
  4.     struct vm_struct tmp_area;  
  5.     struct page **page;  
  6.     struct mm_struct *mm;  
  7.     //以页为单位分配物理页面,由于此时的end=start+PAGE_SIZE,因此只会循环一次  
  8.     for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {  
  9.         int ret;  
  10.         struct page **page_array_ptr;  
  11.         page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];  
  12.   
  13.         //分配物理页面  
  14.         *page = alloc_page(GFP_KERNEL | __GFP_ZERO);  
  15.         if (*page == NULL) {  
  16.             goto err_alloc_page_failed;  
  17.         }  
  18.         tmp_area.addr = page_addr;  
  19.         tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;  
  20.         page_array_ptr = page;  
  21.   
  22.         //把这个物理页面插入到内核空间去  
  23.         ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);  
  24.         if (ret) {  
  25.             goto err_map_kernel_failed;  
  26.         }  
  27.         user_page_addr = (uintptr_t)page_addr + proc->user_buffer_offset;  
  28.   
  29.         //将这个物理页面插入到进程地址空间去  
  30.         ret = vm_insert_page(vma, user_page_addr, page[0]);  
  31.         if (ret) {  
  32.             goto err_vm_insert_page_failed;  
  33.         }  
  34.     }  
  35.     return 0;  
  36. }  
        那么,为什么要这么映射地址呢?
         这恰恰是Binder通讯的精华所在:当把同一块物理页面同时映射到进程空间和内核空间时,当需要在两者之间传递数据时,只需要其中任意一方把数据拷贝到物理页面,另一方直接读取即可,也就是说,数据的跨进程传递,只需要一次拷贝就可以完成。
        我们用图来对比一下这种设计思路的优势。

        首先看一下正常情况下数据跨进程传递的过程:


        这个过程中,至少需要两次拷贝的动作,然后再来看Binder的拷贝机制:


        我们发现Binder通讯中只需要执行一次拷贝的动作就可以完成数据的传递,这样的设计大大提高了数据传递的效率。


五、与客户端交换数据的过程

        对Binder驱动来说,无论是ServiceManager还是Service和Client,都是客户端。我们在ServiceManager流程的介绍中看到,他与Binder驱动的数据交换是通过ioctl()实现的:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {  
  2.     int ret;  
  3.     //得到当前客户端的数据  
  4.     struct binder_proc *proc = filp->private_data;  
  5.     struct binder_thread *thread;  
  6.   
  7.     //得到当前线程的binder_thread信息  
  8.     thread = binder_get_thread(proc);  
  9.     switch (cmd) {  
  10.         case BINDER_WRITE_READ:{}  
  11.         case BINDER_SET_MAX_THREADS:{}  
  12.         case BINDER_SET_CONTEXT_MGR:{}  
  13.         case BINDER_THREAD_EXIT:{}  
  14.         case BINDER_VERSION:{}  
  15.         default:{}  
  16.     }  
  17.     ret = 0;  
  18.     return ret;  
  19. }  

        从这个函数的case中我们看到,他可以实现读写、设置“管理员”、设置最大线程数、退出线程、查询版本等操作,其中最有用的就是BINDER_WRITE_READBINDER_SET_CONTEXT_MGR这两个分支。


5.1、ioctl之BINDER_WRITE_READ

        我们先来看BINDER_WRITE_READ这个处理:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {  
  2.     int ret;  
  3.     thread = binder_get_thread(proc);  
  4.     switch (cmd) {  
  5.         case BINDER_WRITE_READ: {  
  6.             struct binder_write_read bwr;  
  7.             //把用户传递进来的参数转换成到bwr中  
  8.             if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {  
  9.                 ret = -EFAULT;  
  10.                 goto err;  
  11.             }  
  12.   
  13.             if (bwr.write_size > 0) {  
  14.                 //写操作  
  15.                 ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);  
  16.             }  
  17.             if (bwr.read_size > 0) {  
  18.                 //读操作  
  19.                 ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);  
  20.                 if (!list_empty(&proc->todo))  
  21.                     wake_up_interruptible(&proc->wait);  
  22.             }  
  23.             //读写成功,把返回值写回给客户端  
  24.             if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {  
  25.             }  
  26.             break;  
  27.         }  
  28.         case BINDER_SET_MAX_THREADS:{}  
  29.         case BINDER_SET_CONTEXT_MGR:{}  
  30.         case BINDER_THREAD_EXIT:{}  
  31.         case BINDER_VERSION:{}  
  32.         default:{}  
  33.     }  
  34.     ret = 0;  
  35.     return ret;  
  36. }  

        我们看到,在ioctl()内部区分了读写的操作,其中写操作是通过binder_thread_write()实现的,读操作是通过binder_thread_read()实现的,我们分开来分析。

5.1.1、ioctl之写操作

        所谓写的操作,主要完成的任务有两种: 1、写入数据;2、设置状态 。我们直接来看负责写操作的函数。
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, void __user *buffer, int size, signed long *consumed) {  
  2.     uint32_t cmd;  
  3.     void __user *ptr = buffer + *consumed;  
  4.     void __user *end = buffer + size;  
  5.     //用while检测是否已经读取所有的命令  
  6.     while (ptr < end && thread->return_error == BR_OK) {  
  7.         if (get_user(cmd, (uint32_t __user *)ptr))  
  8.             return -EFAULT;  
  9.         ptr += sizeof(uint32_t);  
  10.         switch (cmd) {  
  11.             case BC_INCREFS:  
  12.             case BC_ACQUIRE:  
  13.             case BC_RELEASE:  
  14.             case BC_DECREFS:{}  
  15.             case BC_INCREFS_DONE:  
  16.             case BC_ACQUIRE_DONE:{}  
  17.             case BC_ATTEMPT_ACQUIRE:{}  
  18.             case BC_ACQUIRE_RESULT:{}  
  19.             case BC_FREE_BUFFER:{}  
  20.             case BC_TRANSACTION:  
  21.             case BC_REPLY:{}  
  22.             case BC_REGISTER_LOOPER:{}  
  23.             case BC_ENTER_LOOPER:{}  
  24.             case BC_EXIT_LOOPER:{}  
  25.             case BC_REQUEST_DEATH_NOTIFICATION:  
  26.             case BC_CLEAR_DEATH_NOTIFICATION:{}  
  27.             case BC_DEAD_BINDER_DONE:{}  
  28.             default:  
  29.         }  
  30.         *consumed = ptr - buffer;  
  31.     }  
  32.     return 0;  
  33. }  
        我们先简要说明一下主要case分支的作用,然后分别做详细分析:
         BC_INCREFS:      用于得到一个Service,而且得到的是Service的弱引用
         BC_ACQUIRE:     与BC_INCREFS类似,得到一个Service,但是得到的是强引用
         BC_ENTER_LOOPER :    Service将要进入Loop状态时,发送此消息
         BC_EXIT_LOOPER:     Service退出Loop状态
        下面我们来详细分析。
5.1.1.1、BC_INCREFS与BC_ACQUIRE
        当一个客户端对ServiceManager请求getService时,ServiceManager将会对Binder驱动发送这两种命令,目的就是把目标Service发送给客户端,同时增加目标Service的引用个数。
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, void __user *buffer, int size, signed long *consumed) {  
  2.     uint32_t cmd;  
  3.     void __user *ptr = buffer + *consumed;  
  4.     void __user *end = buffer + size;  
  5.   
  6.     while (ptr < end && thread->return_error == BR_OK) {  
  7.         //得到当前的命令号  
  8.         if (get_user(cmd, (uint32_t __user *)ptr))  
  9.             return -EFAULT;  
  10.         ptr += sizeof(uint32_t);  
  11.         switch (cmd) {  
  12.             case BC_INCREFS:  
  13.             case BC_ACQUIRE:  
  14.             case BC_RELEASE:  
  15.             case BC_DECREFS: {  
  16.                 uint32_t target;  
  17.                 struct binder_ref *ref;  
  18.                 //get_user()的作用是复制内存  
  19.                 if (get_user(target, (uint32_t __user *)ptr))  
  20.                     return -EFAULT;  
  21.                 ptr += sizeof(uint32_t);  
  22.                 if (target == 0 && binder_context_mgr_node && (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {  
  23.                     //客户端要取得的Service是ServiceManager  
  24.                     ref = binder_get_ref_for_node(proc, binder_context_mgr_node);  
  25.                 } else{  
  26.                     //客户端要取得其他的Service  
  27.                     ref = binder_get_ref(proc, target);  
  28.                 }  
  29.                 switch (cmd) {  
  30.                     case BC_INCREFS:  
  31.                         //增加当前Service的弱引用  
  32.                         binder_inc_ref(ref, 0, NULL);  
  33.                         break;  
  34.                     case BC_ACQUIRE:  
  35.                         //增加当前Service的强引用  
  36.                         binder_inc_ref(ref, 1, NULL);  
  37.                         break;  
  38.                     case BC_RELEASE:  
  39.                         //释放引用  
  40.                         binder_dec_ref(ref, 1);  
  41.                         break;  
  42.                     case BC_DECREFS:  
  43.                     default:  
  44.                         binder_dec_ref(ref, 0);  
  45.                         break;  
  46.                 }  
  47.                 break;  
  48.             }  
  49.             default:  
  50.                 return -EINVAL;  
  51.         }  
  52.         *consumed = ptr - buffer;  
  53.     }  
  54.     return 0;  
  55. }  
        在这个过程中,先通过get_user()函数得到要get的Service名字,然后再利用binder_get_refXXX()函数得到该Service的引用,最后需要用binder_inc_ref()增加该Service的引用(强引用或弱引用)。
        但是在得到目标Service的过程中出现了两种情况,第一种情况是,Client要得到的Service正好是ServiceManager这个“Service”,第二种情况是,Client要得到的Service是普通的Service。
        对于要得到ServiceManager这种情况,需要通过binder_get_ref_for_node()的方式从Binder的binder_proc红黑树中查找binder_context_mgr_node的节点,并把表示ServiceManager的节点传递给Client。
        而对于普通的Service这种情况,与ServiceManager类似,需要通过binder_get_ref()的函数从Binder的binder_proc红黑树中查找相应的节点:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static struct binder_ref *binder_get_ref(struct binder_proc *proc, uint32_t desc) {  
  2.     struct rb_node *n = proc->refs_by_desc.rb_node;  
  3.     struct binder_ref *ref;  
  4.     //从红黑树中查找目标Service的节点  
  5.     while (n) {  
  6.         ref = rb_entry(n, struct binder_ref, rb_node_desc);  
  7.         if (desc < ref->desc)  
  8.             n = n->rb_left;  
  9.         else if (desc > ref->desc)  
  10.             n = n->rb_right;  
  11.         else  
  12.             return ref;  
  13.     }  
  14.     return NULL;  
  15. }  
        完成了以上查找的任务后,就要根据当前是弱引用还是强引用去增加目标Service的引用个数了,无论是哪种引用,都是通过binder_inc_ref()函数来实现的,只是参数不同而已:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static int binder_inc_ref(struct binder_ref *ref, int strong, struct list_head *target_list) {  
  2.     int ret;  
  3.     //不同的参数决定了是强引用还是弱引用  
  4.     if (strong) {  
  5.         //强引用  
  6.         if (ref->strong == 0) {  
  7.             ret = binder_inc_node(ref->node, 11, target_list);  
  8.             if (ret)  
  9.                 return ret;  
  10.         }  
  11.         ref->strong++;  
  12.     } else {  
  13.         //弱引用  
  14.         if (ref->weak == 0) {  
  15.             ret = binder_inc_node(ref->node, 01, target_list);  
  16.             if (ret)  
  17.                 return ret;  
  18.         }  
  19.         ref->weak++;  
  20.     }  
  21.     return 0;  
  22. }  
        经过以上的过程,就完成了整个binder_thread_write操作中的BC_INCREFS和BC_ACQUIRE分支。
5.1.1.2、BC_ENTER_LOOPER与BC_EXIT_LOOPER
        下面我们来分析关于进退Loop状态的两种binder_thread_write两种case。
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, void __user *buffer, int size, signed long *consumed) {  
  2.     uint32_t cmd;  
  3.     void __user *ptr = buffer + *consumed;  
  4.     void __user *end = buffer + size;  
  5.   
  6.     while (ptr < end && thread->return_error == BR_OK) {  
  7.         if (get_user(cmd, (uint32_t __user *)ptr))  
  8.             return -EFAULT;  
  9.         ptr += sizeof(uint32_t);  
  10.         switch (cmd) {  
  11.             case BC_ENTER_LOOPER:  
  12.                 //进入Loop状态  
  13.                 if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {  
  14.                     thread->looper |= BINDER_LOOPER_STATE_INVALID;  
  15.                 }  
  16.                 //thread->looper值变为BINDER_LOOPER_STATE_ENTERED了  
  17.                 thread->looper |= BINDER_LOOPER_STATE_ENTERED;  
  18.                 break;  
  19.             case BC_EXIT_LOOPER:  
  20.                 //退出Loop状态  
  21.                 thread->looper |= BINDER_LOOPER_STATE_EXITED;  
  22.                 break;  
  23.   
  24.   
  25.             default:  
  26.                 return -EINVAL;  
  27.         }  
  28.         *consumed = ptr - buffer;  
  29.     }  
  30.     return 0;  
  31. }  
        从这个过程我们看到,进入和退出Loop状态只需要将当前binder_thread的looper标志位置位或者重置即可。
        至此,我们分析了ioctl的写操作的过程,主要是针对其内部四个主要的消息进行分析,分别是BC_INCREFS、BC_ACQUIRE、BC_ENTER_LOOPER、BC_EXIT_LOOPER。

5.1.2、ioctl之读操作

        下面我们来分析ioctl的读操作:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, void  __user *buffer, int size, signed long *consumed, int non_block) {  
  2. retry:  
  3.     //判断当前是否有内容需要读取  
  4.     wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);  
  5.     mutex_unlock(&binder_lock);  
  6.     //根据线程阻塞模式和当前的未读信息判断是否阻塞等待  
  7.     if (wait_for_proc_work) {  
  8.         //设置当前优先级与线程自己的优先级相同  
  9.         binder_set_nice(proc->default_priority);  
  10.   
  11.         if (non_block) {  
  12.             if (!binder_has_proc_work(proc, thread))  
  13.                 ret = -EAGAIN;  
  14.         } else{  
  15.             ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));  
  16.         }  
  17.     } else {  
  18.         if (non_block) {  
  19.             if (!binder_has_thread_work(thread))  
  20.                 ret = -EAGAIN;  
  21.         } else  
  22.             ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));  
  23.     }  
  24.     mutex_lock(&binder_lock);  
  25.     //进入读取模式  
  26.     thread->looper &= ~BINDER_LOOPER_STATE_WAITING;  
  27.   
  28.     while (1) {  
  29.         uint32_t cmd;  
  30.         struct binder_transaction_data tr;  
  31.         struct binder_work *w;  
  32.         struct binder_transaction *t = NULL;  
  33.   
  34.         //读取todo列表中的节点  
  35.         if (!list_empty(&thread->todo))  
  36.             w = list_first_entry(&thread->todo, struct binder_work, entry);  
  37.         else if (!list_empty(&proc->todo) && wait_for_proc_work)  
  38.             w = list_first_entry(&proc->todo, struct binder_work, entry);  
  39.         else {  
  40.             if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))  
  41.                 goto retry;  
  42.             break;  
  43.         }  
  44.         switch (w->type) {  
  45.             case BINDER_WORK_TRANSACTION: {  
  46.                 t = container_of(w, struct binder_transaction, work);  
  47.             } break;  
  48.             case BINDER_WORK_TRANSACTION_COMPLETE: {  
  49.                 cmd = BR_TRANSACTION_COMPLETE;  
  50.                 //将读取的数据放入用户空间  
  51.                 if (put_user(cmd, (uint32_t __user *)ptr))  
  52.                     return -EFAULT;  
  53.                 ptr += sizeof(uint32_t);  
  54.   
  55.                 binder_stat_br(proc, thread, cmd);  
  56.                 //删除当前节点  
  57.                 list_del(&w->entry);  
  58.                 kfree(w);  
  59.                 binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);  
  60.             } break;  
  61.             case BINDER_WORK_NODE: {} break;  
  62.             case BINDER_WORK_DEAD_BINDER:  
  63.             case BINDER_WORK_DEAD_BINDER_AND_CLEAR:  
  64.             case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {} break;  
  65.         }  
  66.     }  
  67. done:  
  68.     return 0;  
  69. }  
        在读的过程中并没有特殊的处理,只是读取数据而已。
        下面用一张图来小结一下ioctl的读写操作流程。

5.2、ioctl之BINDER_SET_CONTEXT_MGR

        ioctl中的BINDER_SET_CONTEXT_MGR分支,只有ServiceManager在binder_become_context_manager()中调用:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int binder_become_context_manager(struct binder_state *bs) {  
  2.     return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);  
  3. }  
        通过这样的操作,ServiceManager可以将自己注册为管理员。下面我们来看详细过程:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.     static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {  
  2.         //得到当前线程的binder_thread信息  
  3.         thread = binder_get_thread(proc);  
  4.   
  5.         switch (cmd) {  
  6.             case BINDER_WRITE_READ: { }  
  7.             case BINDER_SET_MAX_THREADS:  
  8.                 break;  
  9.             case BINDER_SET_CONTEXT_MGR:  
  10.                 //确认当前没有ServiceManager的实体  
  11.                 if (binder_context_mgr_node != NULL) {  
  12.                     printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");  
  13.                     ret = -EBUSY;  
  14.                     goto err;  
  15.                 }  
  16.                 //初始化binder_context_mgr_uid为当前进程的uid  
  17.                 if (binder_context_mgr_uid != -1) {  
  18.                     if (binder_context_mgr_uid != current->cred->euid) {  
  19.                         //确保uid为当前进程的uid  
  20.                         ret = -EPERM;  
  21.                         goto err;  
  22.                     }  
  23.                 } else{  
  24.                     binder_context_mgr_uid = current->cred->euid;  
  25.                 }  
  26.                 //为ServiceManager创建Binder实体  
  27.                 binder_context_mgr_node = binder_new_node(proc, NULL, NULL);  
  28.                 if (binder_context_mgr_node == NULL) {  
  29.                     ret = -ENOMEM;  
  30.                     goto err;  
  31.                 }  
  32.                 //初始化强引用和弱引用的个数  
  33.                 binder_context_mgr_node->local_weak_refs++;  
  34.                 binder_context_mgr_node->local_strong_refs++;  
  35.                 binder_context_mgr_node->has_strong_ref = 1;  
  36.                 binder_context_mgr_node->has_weak_ref = 1;  
  37.                 break;  
  38.             case BINDER_THREAD_EXIT:  
  39.                 break;  
  40.             case BINDER_VERSION:  
  41.                 break;  
  42.             default:  
  43.                 goto err;  
  44.         }  
  45.         ret = 0;  
  46. err:  
  47.         if (thread) thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;  
  48.         mutex_unlock(&binder_lock);  
  49.         wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);  
  50.         return ret;  
  51.     }  
        我们看到,BINDER_SET_CONTEXT_MGR的过程就是创建binder_context_mgr_node节点,并且设置binder_context_mgr_uid为ServiceManager的uid的过程。
        现在我们回顾一下5.1.1.1节中介绍的ioctl函数中请求BC_INCREFS与BC_ACQUIRE的命令,当客户端请求该命令时,即要求得到某个Service,此时就要区分客户端得到的Service是否是ServiceManager,如果要得到的Service就是ServiceManager,那么就需要去红黑树中查找binder_context_mgr_node节点,而这个节点就是ServiceManager在Binder驱动中的节点。否则的话,就用目标Service名字去查找相应的节点即可。
        这就是ioctl中BINDER_SET_CONTEXT_MGR分支所做的工作。

        至此,Binder驱动部分的主要原理也介绍完毕,经过这一节的分析,我们了解了Binder驱动设备的创建、打开过程,以及其他进程是如何与Binder设备交换数据的。


原文地址:http://blog.csdn.net/u010961631/article/details/20479507

Android BinderAndroid操作系统中的一个IPC(进程间通信)机制,用于实现进程之间的通信和数据传输。Binder源码主要位于frameworks/native目录下。 在Binder源码中,最核心的部分是Binder驱动Binder服务。Binder驱动是位于内核空间的组件,负责处理进程间的数据传输和交互。Binder服务是位于用户空间的组件,负责提供接口和功能来进行进程间通信。 在Binder源码中,主要涉及到以下几个重要的文件和目录: 1. drivers目录:包含了Binder驱动的代码,其中最重要的文件是binder.c,它实现了Binder驱动的核心逻辑。 2. include目录:包含了Binder的头文件,其中最重要的文件是binder.h,它定义了Binder的接口和数据结构。 3. libbinder目录:包含了Binder服务的代码,其中最重要的文件是IBinder.cpp和BpBinder.cpp,它们分别实现了Binder服务的接口和代理类。 4. services目录:包含了一些系统级别的Binder服务,例如Package Manager Service和Activity Manager Service。 如果你想深入了解Android Binder源码,可以参考以下资源: 1. Android 源码:你可以从Android官网或者GitHub上获取Android源码,并在frameworks/native目录下查看Binder相关的代码。 2. Android系统架构指南:Android官网提供了关于Android系统架构的详细文档,其中有关IPC和Binder的章节对于理解Binder的实现理和源码结构很有帮助。 3. 《深入理解Android:卷2》一书中有关于Binder的详细介绍和源码解析,可以作为参考资料。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值