浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路
原文地址: http://blog.csdn.net/luoshengyang/article/details/6621566
上一篇文章Android进程间通信(IPC)机制Binder简要介绍和学习计划简要介绍了Android系统进程间通信机制Binder的总体架构,它由Client、Server、Service Manager和驱动程序Binder四个组件构成。本文着重介绍组件Service Manager,它是整个Binder机制的守护进程,用来管理开发者创建的各种Server,并且向Client提供查询Server远程接口的功能。
既然Service Manager组件是用来管理Server并且向Client提供查询Server远程接口的功能,那么,Service Manager就必然要和Server以及Client进行通信了。我们知道,Service Manger、Client和Server三者分别是运行在独立的进程当中,这样它们之间的通信也属于进程间通信了,而且也是采用Binder机制进行进程间通信,因此,Service Manager在充当Binder机制的守护进程的角色的同时,也在充当Server的角色,然而,它是一种特殊的Server,下面我们将会看到它的特殊之处。
与Service Manager相关的源代码较多,这里不会完整去分析每一行代码,主要是带着Service Manager是如何成为整个Binder机制中的守护进程这条主线来一步一步地深入分析相关源代码,包括从用户空间到内核空间的相关源代码。希望读者在阅读下面的内容之前,先阅读一下前一篇文章提到的两个参考资料Android深入浅出之Binder机制和Android Binder设计与实现,熟悉相关概念和数据结构,这有助于理解下面要分析的源代码。
Service Manager在用户空间的源代码位于frameworks/base/cmds/servicemanager目录下,主要是由binder.h、binder.c和service_manager.c三个文件组成。Service Manager的入口位于service_manager.c文件中的main函数:
- intmain(intargc,char**argv)
- {
- structbinder_state *bs;
- void*svcmgr = BINDER_SERVICE_MANAGER;
- bs = binder_open(128*1024);
- if(binder_become_context_manager(bs)) {
- LOGE("cannot become context manager (%s)\n", strerror(errno));
- return-1;
- }
- svcmgr_handle = svcmgr;
- binder_loop(bs, svcmgr_handler);
- return0;
- }
main函数主要有三个功能:一是打开Binder设备文件;二是告诉Binder驱动程序自己是Binder上下文管理者,即我们前面所说的守护进程;三是进入一个无穷循环,充当Server的角色,等待Client的请求。进入这三个功能之间,先来看一下这里用到的结构体binder_state、宏BINDER_SERVICE_MANAGER的定义:
struct binder_state定义在frameworks/base/cmds/servicemanager/binder.c文件中:
- structbinder_state
- {
- intfd;
- void*mapped;
- unsigned mapsize;
- };
fd是文件描述符,即表示打开的/dev/binder设备文件描述符;mapped是把设备文件/dev/binder映射到进程空间的起始地址;mapsize是上述内存映射空间的大小。
宏BINDER_SERVICE_MANAGER定义frameworks/base/cmds/servicemanager/binder.h文件中:
- /* the one magic object */
- #define BINDER_SERVICE_MANAGER ((void*) 0)
它表示Service Manager的句柄为0。Binder通信机制使用句柄来代表远程接口,这个句柄的意义和Windows编程中用到的句柄是差不多的概念。前面说到,Service Manager在充当守护进程的同时,它充当Server的角色,当它作为远程接口使用时,它的句柄值便为0,这就是它的特殊之处,其余的Server的远程接口句柄值都是一个大于0 而且由Binder驱动程序自动进行分配的。
函数首先是执行打开Binder设备文件的操作binder_open,这个函数位于frameworks/base/cmds/servicemanager/binder.c文件中:
- structbinder_state *binder_open(unsigned mapsize)
- {
- structbinder_state *bs;
- bs = malloc(sizeof(*bs));
- if(!bs) {
- errno = ENOMEM;
- return0;
- }
- bs->fd = open("/dev/binder", O_RDWR);
- if(bs->fd < 0) {
- fprintf(stderr,"binder: cannot open device (%s)\n",
- strerror(errno));
- gotofail_open;
- }
- bs->mapsize = mapsize;
- bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
- if(bs->mapped == MAP_FAILED) {
- fprintf(stderr,"binder: cannot map device (%s)\n",
- strerror(errno));
- gotofail_map;
- }
- /* TODO: check version */
- returnbs;
- fail_map:
- close(bs->fd);
- fail_open:
- free(bs);
- return0;
- }
通过文件操作函数open来打开/dev/binder设备文件。设备文件/dev/binder是在Binder驱动程序模块初始化的时候创建的,我们先看一下这个设备文件的创建过程。进入到kernel/common/drivers/staging/android目录中,打开binder.c文件,可以看到模块初始化入口binder_init:
- staticstructfile_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,
- };
- staticstructmiscdevice binder_miscdev = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "binder",
- .fops = &binder_fops
- };
- staticint__init binder_init(void)
- {
- intret;
- binder_proc_dir_entry_root = proc_mkdir("binder", NULL);
- if(binder_proc_dir_entry_root)
- binder_proc_dir_entry_proc = proc_mkdir("proc", binder_proc_dir_entry_root);
- ret = misc_register(&binder_miscdev);
- if(binder_proc_dir_entry_root) {
- create_proc_read_entry("state", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_state, NULL);
- create_proc_read_entry("stats", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_stats, NULL);
- create_proc_read_entry("transactions", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_transactions, NULL);
- create_proc_read_entry("transaction_log", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_transaction_log, &binder_transaction_log);
- create_proc_read_entry("failed_transaction_log", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_transaction_log, &binder_transaction_log_failed);
- }
- returnret;
- }
- device_initcall(binder_init);
创建设备文件的地方在misc_register函数里面,关于misc设备的注册,我们在Android日志系统驱动程序Logger源代码分析一文中有提到,有兴趣的读取不访去了解一下。其余的逻辑主要是在/proc目录创建各种Binder相关的文件,供用户访问。从设备文件的操作方法binder_fops可以看出,前面的binder_open函数执行语句:
- bs->fd = open("/dev/binder", O_RDWR);
就进入到Binder驱动程序的binder_open函数了:
- staticintbinder_open(structinode *nodp,structfile *filp)
- {
- structbinder_proc *proc;
- if(binder_debug_mask & BINDER_DEBUG_OPEN_CLOSE)
- printk(KERN_INFO "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);
- init_waitqueue_head(&proc->wait);
- proc->default_priority = task_nice(current);
- mutex_lock(&binder_lock);
- binder_stats.obj_created[BINDER_STAT_PROC]++;
- hlist_add_head(&proc->proc_node, &binder_procs);
- proc->pid = current->group_leader->pid;
- INIT_LIST_HEAD(&proc->delivered_death);
- filp->private_data = proc;
- mutex_unlock(&binder_lock);
- if(binder_proc_dir_entry_proc) {
- charstrbuf[11];
- snprintf(strbuf, sizeof(strbuf),"%u", proc->pid);
- remove_proc_entry(strbuf, binder_proc_dir_entry_proc);
- create_proc_read_entry(strbuf, S_IRUGO, binder_proc_dir_entry_proc, binder_read_proc_proc, proc);
- }
- return0;
- }
这个函数的主要作用是创建一个struct binder_proc数据结构来保存打开设备文件/dev/binder的进程的上下文信息,并且将这个进程上下文信息保存在打开文件结构struct file的私有数据成员变量private_data中,这样,在执行其它文件操作时,就通过打开文件结构struct file来取回这个进程上下文信息了。这个进程上下文信息同时还会保存在一个全局哈希表binder_procs中,驱动程序内部使用。binder_procs定义在文件的开头:
- staticHLIST_HEAD(binder_procs);
结构体struct binder_proc也是定义在kernel/common/drivers/staging/android/binder.c文件中:
- structbinder_proc {
- structhlist_node proc_node;
- structrb_root threads;
- structrb_root nodes;
- structrb_root refs_by_desc;
- structrb_root refs_by_node;
- intpid;
- structvm_area_struct *vma;
- structtask_struct *tsk;
- structfiles_struct *files;
- structhlist_node deferred_work_node;
- intdeferred_work;
- void*buffer;
- ptrdiff_tuser_buffer_offset;
- structlist_head buffers;
- structrb_root free_buffers;
- structrb_root allocated_buffers;
- size_tfree_async_space;
- structpage **pages;
- size_tbuffer_size;
- uint32_t buffer_free;
- structlist_head todo;
- wait_queue_head_t wait;
- structbinder_stats stats;
- structlist_head delivered_death;
- intmax_threads;
- intrequested_threads;
- intrequested_threads_started;
- intready_threads;
- longdefault_priority;
- };
这个结构体的成员比较多,这里就不一一说明了,简单解释一下四个成员变量threads、nodes、 refs_by_desc和refs_by_node,其它的我们在遇到的时候再详细解释。这四个成员变量都是表示红黑树的节点,也就是说,binder_proc分别挂会四个红黑树下。threads树用来保存binder_proc进程内用于处理用户请求的线程,它的最大数量由max_threads来决定;node树成用来保存binder_proc进程内的Binder实体;refs_by_desc树和refs_by_node树用来保存binder_proc进程内的Binder引用,即引用的其它进程的Binder实体,它分别用两种方式来组织红黑树,一种是以句柄作来key值来组织,一种是以引用的实体节点的地址值作来key值来组织,它们都是表示同一样东西,只不过是为了内部查找方便而用两个红黑树来表示。
这样,打开设备文件/dev/binder的操作就完成了,接着是对打开的设备文件进行内存映射操作mmap:
- bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
对应Binder驱动程序的binder_mmap函数:
- staticintbinder_mmap(structfile *filp,structvm_area_struct *vma)
- {
- intret;
- structvm_struct *area;
- structbinder_proc *proc = filp->private_data;
- constchar*failure_string;
- structbinder_buffer *buffer;
- if((vma->vm_end - vma->vm_start) > SZ_4M)
- vma->vm_end = vma->vm_start + SZ_4M;
- if(binder_debug_mask & BINDER_DEBUG_OPEN_CLOSE)
- printk(KERN_INFO
- "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) {
- ret = -EPERM;
- failure_string = "bad vm_flags";
- gotoerr_bad_arg;
- }
- vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
- if(proc->buffer) {
- ret = -EBUSY;
- failure_string = "already mapped";
- gotoerr_already_mapped;
- }
- area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
- if(area == NULL) {
- ret = -ENOMEM;
- failure_string = "get_vm_area";
- gotoerr_get_vm_area_failed;
- }
- proc->buffer = area->addr;
- proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
- #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";
- gotoerr_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)) {
- ret = -ENOMEM;
- failure_string = "alloc small buf";
- gotoerr_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(current);
- proc->vma = vma;
- /*printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
- return0;
- err_alloc_small_buf_failed:
- kfree(proc->pages);
- proc->pages = NULL;
- err_alloc_pages_failed:
- vfree(proc->buffer);
- proc->buffer = NULL;
- err_get_vm_area_failed:
- err_already_mapped:
- 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);
- returnret;
- }
函数首先通过filp->private_data得到在打开设备文件/dev/binder时创建的struct binder_proc结构。内存映射信息放在vma参数中,注意,这里的vma的数据类型是struct vm_area_struct,它表示的是一块连续的虚拟地址空间区域,在函数变量声明的地方,我们还看到有一个类似的结构体struct vm_struct,这个数据结构也是表示一块连续的虚拟地址空间区域,那么,这两者的区别是什么呢?在Linux中,struct vm_area_struct表示的虚拟地址是给进程使用的,而struct vm_struct表示的虚拟地址是给内核使用的,它们对应的物理页面都可以是不连续的。struct vm_area_struct表示的地址空间范围是0~3G,而struct vm_struct表示的地址空间范围是(3G + 896M + 8M) ~ 4G。struct vm_struct表示的地址空间范围为什么不是3G~4G呢?原来,3G ~ (3G + 896M)范围的地址是用来映射连续的物理页面的,这个范围的虚拟地址和对应的实际物理地址有着简单的对应关系,即对应0~896M的物理地址空间,而(3G + 896M) ~ (3G + 896M + 8M)是安全保护区域(例如,所有指向这8M地址空间的指针都是非法的),因此struct vm_struct使用(3G + 896M + 8M) ~ 4G地址空间来映射非连续的物理页面。有关Linux的内存管理知识,可以参考Android学习启动篇一文提到的《Understanding the Linux Kernel》一书中的第8章。
这里为什么会同时使用进程虚拟地址空间和内核虚拟地址空间来映射同一个物理页面呢?这就是Binder进程间通信机制的精髓所在了,同一个物理页面,一方映射到进程虚拟地址空间,一方面映射到内核虚拟地址空间,这样,进程和内核之间就可以减少一次内存拷贝了,提到了进程间通信效率。举个例子如,Client要将一块内存数据传递给Server,一般的做法是,Client将这块数据从它的进程空间拷贝到内核空间中,然后内核再将这个数据从内核空间拷贝到Server的进程空间,这样,Server就可以访问这个数据了。但是在这种方法中,执行了两次内存拷贝操作,而采用我们上面提到的方法,只需要把Client进程空间的数据拷贝一次到内核空间,然后Server与内核共享这个数据就可以了,整个过程只需要执行一次内存拷贝,提高了效率。
binder_mmap的原理讲完了,这个函数的逻辑就好理解了。不过,这里还是先要解释一下struct binder_proc结构体的几个成员变量。buffer成员变量是一个void*指针,它表示要映射的物理内存在内核空间中的起始位置;buffer_size成员变量是一个size_t类型的变量,表示要映射的内存的大小;pages成员变量是一个struct page*类型的数组,struct page是用来描述物理页面的数据结构;user_buffer_offset成员变量是一个ptrdiff_t类型的变量,它表示的是内核使用的虚拟地址与进程使用的虚拟地址之间的差值,即如果某个物理页面在内核空间中对应的虚拟地址是addr的话,那么这个物理页面在进程空间对应的虚拟地址就为addr + user_buffer_offset。
再解释一下Binder驱动程序管理这个内存映射地址空间的方法,即是如何管理buffer ~ (buffer + buffer_size)这段地址空间的,这个地址空间被划分为一段一段来管理,每一段是结构体struct binder_buffer来描述:
- structbinder_buffer {
- structlist_head entry;/* free and allocated entries by addesss */
- structrb_node rb_node;/* free entry by size or allocated entry */
- /* by address */
- unsigned free : 1;
- unsigned allow_user_free : 1;
- unsigned async_transaction : 1;
- unsigned debug_id : 29;
- structbinder_transaction *transaction;
- structbinder_node *target_node;
- size_tdata_size;
- size_toffsets_size;
- uint8_t data[0];
- };
每一个binder_buffer通过其成员entry按从低址到高地址连入到struct binder_proc中的buffers表示的链表中去,同时,每一个binder_buffer又分为正在使用的和空闲的,通过free成员变量来区分,空闲的binder_buffer通过成员变量rb_node连入到struct binder_proc中的free_buffers表示的红黑树中去,正在使用的binder_buffer通过成员变量rb_node连入到struct binder_proc中的allocated_buffers表示的红黑树中去。这样做当然是为了方便查询和维护这块地址空间了,这一点我们可以从其它的代码中看到,等遇到的时候我们再分析。
终于可以回到binder_mmap这个函数来了,首先是对参数作一些健康体检(sanity check),例如,要映射的内存大小不能超过SIZE_4M,即4M,回到service_manager.c中的main 函数,这里传进来的值是128 * 1024个字节,即128K,这个检查没有问题。通过健康体检后,调用get_vm_area函数获得一个空闲的vm_struct区间,并初始化proc结构体的buffer、user_buffer_offset、pages和buffer_size和成员变量,接着调用binder_update_page_range来为虚拟地址空间proc->buffer ~ proc->buffer + PAGE_SIZE分配一个空闲的物理页面,同时这段地址空间使用一个binder_buffer来描述,分别插入到proc->buffers链表和proc->free_buffers红黑树中去,最后,还初始化了proc结构体的free_async_space、files和vma三个成员变量。
这里,我们继续进入到binder_update_page_range函数中去看一下Binder驱动程序是如何实现把一个物理页面同时映射到内核空间和进程空间去的:
- staticintbinder_update_page_range(structbinder_proc *proc,intallocate,
- void*start,void*end,structvm_area_struct *vma)
- {
- void*page_addr;
- unsigned longuser_page_addr;
- structvm_struct tmp_area;
- structpage **page;
- structmm_struct *mm;
- if(binder_debug_mask & BINDER_DEBUG_BUFFER_ALLOC)
- printk(KERN_INFO "binder: %d: %s pages %p-%p\n",
- proc->pid, allocate ? "allocate":"free", start, end);
- if(end <= start)
- return0;
- if(vma)
- mm = NULL;
- else
- mm = get_task_mm(proc->tsk);
- if(mm) {
- down_write(&mm->mmap_sem);
- vma = proc->vma;
- }
- if(allocate == 0)
- gotofree_range;
- if(vma == NULL) {
- printk(KERN_ERR "binder: %d: binder_alloc_buf failed to "
- "map pages in userspace, no vma\n", proc->pid);
- gotoerr_no_vma;
- }
- for(page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
- intret;
- structpage **page_array_ptr;
- page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
- BUG_ON(*page);
- *page = alloc_page(GFP_KERNEL | __GFP_ZERO);
- if(*page == NULL) {
- printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
- "for page at %p\n", proc->pid, page_addr);
- gotoerr_alloc_page_failed;
- }
- tmp_area.addr = page_addr;
- tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;
- page_array_ptr = page;
- ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);
- if(ret) {
- printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
- "to map page at %p in kernel\n",
- proc->pid, page_addr);
- gotoerr_map_kernel_failed;
- }
- user_page_addr =
- (uintptr_t)page_addr + proc->user_buffer_offset;
- ret = vm_insert_page(vma, user_page_addr, page[0]);
- if(ret) {
- printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
- "to map page at %lx in userspace\n",
- proc->pid, user_page_addr);
- gotoerr_vm_insert_page_failed;
- }
- /* vm_insert_page does not seem to increment the refcount */
- }
- if(mm) {
- up_write(&mm->mmap_sem);
- mmput(mm);
- }
- return0;
- free_range:
- for(page_addr = end - PAGE_SIZE; page_addr >= start;
- page_addr -= PAGE_SIZE) {
- page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
- if(vma)
- zap_page_range(vma, (uintptr_t)page_addr +
- proc->user_buffer_offset, PAGE_SIZE, NULL);
- err_vm_insert_page_failed:
- unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
- err_map_kernel_failed:
- __free_page(*page);
- *page = NULL;
- err_alloc_page_failed:
- ;
- }
- err_no_vma:
- if(mm) {
- up_write(&mm->mmap_sem);
- mmput(mm);
- }
- return-ENOMEM;
- }
这个函数既可以分配物理页面,也可以用来释放物理页面,通过allocate参数来区别,这里我们只关注分配物理页面的情况。要分配物理页面的虚拟地址空间范围为(start ~ end),函数前面的一些检查逻辑就不看了,直接看中间的for循环:
- for(page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
- intret;
- structpage **page_array_ptr;
- page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
- BUG_ON(*page);
- *page = alloc_page(GFP_KERNEL | __GFP_ZERO);
- if(*page == NULL) {
- printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
- "for page at %p\n", proc->pid, page_addr);
- gotoerr_alloc_page_failed;
- }
- tmp_area.addr = page_addr;
- tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;
- page_array_ptr = page;
- ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);
- if(ret) {
- printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
- "to map page at %p in kernel\n",
- proc->pid, page_addr);
- gotoerr_map_kernel_failed;
- }
- user_page_addr =
- (uintptr_t)page_addr + proc->user_buffer_offset;
- ret = vm_insert_page(vma, user_page_addr, page[0]);
- if(ret) {
- printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
- "to map page at %lx in userspace\n",
- proc->pid, user_page_addr);
- gotoerr_vm_insert_page_failed;
- }
- /* vm_insert_page does not seem to increment the refcount */
- }
首先是调用alloc_page来分配一个物理页面,这个函数返回一个struct page物理页面描述符,根据这个描述的内容初始化好struct vm_struct tmp_area结构体,然后通过map_vm_area将这个物理页面插入到tmp_area描述的内核空间去,接着通过page_addr + proc->user_buffer_offset获得进程虚拟空间地址,并通过vm_insert_page函数将这个物理页面插入到进程地址空间去,参数vma代表了要插入的进程的地址空间。
这样,frameworks/base/cmds/servicemanager/binder.c文件中的binder_open函数就描述完了,回到frameworks/base/cmds/servicemanager/service_manager.c文件中的main函数,下一步就是调用binder_become_context_manager来通知Binder驱动程序自己是Binder机制的上下文管理者,即守护进程。binder_become_context_manager函数位于frameworks/base/cmds/servicemanager/binder.c文件中:
- intbinder_become_context_manager(structbinder_state *bs)
- {
- returnioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
- }
这里通过调用ioctl文件操作函数来通知Binder驱动程序自己是守护进程,命令号是BINDER_SET_CONTEXT_MGR,没有参数。BINDER_SET_CONTEXT_MGR定义为:
- #define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int)
这样就进入到Binder驱动程序的binder_ioctl函数,我们只关注BINDER_SET_CONTEXT_MGR命令:
- staticlongbinder_ioctl(structfile *filp, unsignedintcmd, unsignedlongarg)
- {
- intret;
- structbinder_proc *proc = filp->private_data;
- structbinder_thread *thread;
- unsigned intsize = _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);*/
- ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
- if(ret)
- returnret;
- mutex_lock(&binder_lock);
- thread= binder_get_thread(proc);
- if(thread== NULL) {
- ret = -ENOMEM;
- gotoerr;
- }
- switch(cmd) {
- ......
- caseBINDER_SET_CONTEXT_MGR:
- if(binder_context_mgr_node != NULL) {
- printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
- ret = -EBUSY;
- gotoerr;
- }
- 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;
- gotoerr;
- }
- } 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;
- gotoerr;
- }
- 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;
- ......
- default:
- ret = -EINVAL;
- gotoerr;
- }
- ret = 0;
- err:
- if(thread)
- thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
- mutex_unlock(&binder_lock);
- 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);
- returnret;
- }
继续分析这个函数之前,又要解释两个数据结构了,一个是struct binder_thread结构体,顾名思久,它表示一个线程,这里就是执行binder_become_context_manager函数的线程了。
- structbinder_thread {
- structbinder_proc *proc;
- structrb_node rb_node;
- intpid;
- intlooper;
- structbinder_transaction *transaction_stack;
- structlist_head todo;
- uint32_t return_error; /* Write failed, return error code in read buf */
- uint32_t return_error2; /* Write failed, return error code in read */
- /* buffer. Used when sending a reply to a dead process that */
- /* we are also waiting on */
- wait_queue_head_t wait;
- structbinder_stats stats;
- };
proc表示这个线程所属的进程。struct binder_proc有一个成员变量threads,它的类型是rb_root,它表示一查红黑树,把属于这个进程的所有线程都组织起来,struct binder_thread的成员变量rb_node就是用来链入这棵红黑树的节点了。looper成员变量表示线程的状态,它可以取下面这几个值:
- enum{
- BINDER_LOOPER_STATE_REGISTERED = 0x01,
- BINDER_LOOPER_STATE_ENTERED = 0x02,
- BINDER_LOOPER_STATE_EXITED = 0x04,
- BINDER_LOOPER_STATE_INVALID = 0x08,
- BINDER_LOOPER_STATE_WAITING = 0x10,
- BINDER_LOOPER_STATE_NEED_RETURN = 0x20
- };
其余的成员变量,transaction_stack表示线程正在处理的事务,todo表示发往该线程的数据列表,return_error和return_error2表示操作结果返回码,wait用来阻塞线程等待某个事件的发生,stats用来保存一些统计信息。这些成员变量遇到的时候再分析它们的作用。
另外一个数据结构是struct binder_node,它表示一个binder实体:
- structbinder_node {
- intdebug_id;
- structbinder_work work;
- union{
- structrb_node rb_node;
- structhlist_node dead_node;
- };
- structbinder_proc *proc;
- structhlist_head refs;
- intinternal_strong_refs;
- intlocal_weak_refs;
- intlocal_strong_refs;
- void__user *ptr;
- void__user *cookie;
- unsigned has_strong_ref : 1;
- unsigned pending_strong_ref : 1;
- unsigned has_weak_ref : 1;
- unsigned pending_weak_ref : 1;
- unsigned has_async_transaction : 1;
- unsigned accept_fds : 1;
- intmin_priority : 8;
- structlist_head async_todo;
- };
rb_node和dead_node组成一个联合体。 如果这个Binder实体还在正常使用,则使用rb_node来连入proc->nodes所表示的红黑树的节点,这棵红黑树用来组织属于这个进程的所有Binder实体;如果这个Binder实体所属的进程已经销毁,而这个Binder实体又被其它进程所引用,则这个Binder实体通过dead_node进入到一个哈希表中去存放。proc成员变量就是表示这个Binder实例所属于进程了。refs成员变量把所有引用了该Binder实体的Binder引用连接起来构成一个链表。internal_strong_refs、local_weak_refs和local_strong_refs表示这个Binder实体的引用计数。ptr和cookie成员变量分别表示这个Binder实体在用户空间的地址以及附加数据。其余的成员变量就不描述了,遇到的时候再分析。
现在回到binder_ioctl函数中,首先是通过filp->private_data获得proc变量,这里binder_mmap函数是一样的。接着通过binder_get_thread函数获得线程信息,我们来看一下这个函数:
- staticstructbinder_thread *binder_get_thread(structbinder_proc *proc)
- {
- structbinder_thread *thread= NULL;
- structrb_node *parent = NULL;
- structrb_node **p = &proc->threads.rb_node;
- while(*p) {
- parent = *p;
- thread= rb_entry(parent,structbinder_thread, rb_node);
- if(current->pid <thread->pid)
- p = &(*p)->rb_left;
- elseif(current->pid >thread->pid)
- p = &(*p)->rb_right;
- else
- break;
- }
- if(*p == NULL) {
- thread= kzalloc(sizeof(*thread), GFP_KERNEL);
- if(thread== NULL)
- returnNULL;
- binder_stats.obj_created[BINDER_STAT_THREAD]++;
- thread->proc = proc;
- thread->pid = current->pid;
- init_waitqueue_head(&thread->wait);
- INIT_LIST_HEAD(&thread->todo);
- rb_link_node(&thread->rb_node, parent, p);
- rb_insert_color(&thread->rb_node, &proc->threads);
- thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
- thread->return_error = BR_OK;
- thread->return_error2 = BR_OK;
- }
- returnthread;
- }
这里把当前线程current的pid作为键值,在进程proc->threads表示的红黑树中进行查找,看是否已经为当前线程创建过了binder_thread信息。在这个场景下,由于当前线程是第一次进到这里,所以肯定找不到,即*p == NULL成立,于是,就为当前线程创建一个线程上下文信息结构体binder_thread,并初始化相应成员变量,并插入到proc->threads所表示的红黑树中去,下次要使用时就可以从proc中找到了。注意,这里的thread->looper = BINDER_LOOPER_STATE_NEED_RETURN。
回到binder_ioctl函数,继续往下面,有两个全局变量binder_context_mgr_node和binder_context_mgr_uid,它定义如下:
- staticstructbinder_node *binder_context_mgr_node;
- staticuid_t binder_context_mgr_uid = -1;
binder_context_mgr_node用来表示Service Manager实体,binder_context_mgr_uid表示Service Manager守护进程的uid。在这个场景下,由于当前线程是第一次进到这里,所以binder_context_mgr_node为NULL,binder_context_mgr_uid为-1,于是初始化binder_context_mgr_uid为current->cred->euid,这样,当前线程就成为Binder机制的守护进程了,并且通过binder_new_node为Service Manager创建Binder实体:
- staticstructbinder_node *
- binder_new_node(structbinder_proc *proc,void__user *ptr,void__user *cookie)
- {
- structrb_node **p = &proc->nodes.rb_node;
- structrb_node *parent = NULL;
- structbinder_node *node;
- while(*p) {
- parent = *p;
- node = rb_entry(parent, structbinder_node, rb_node);
- if(ptr < node->ptr)
- p = &(*p)->rb_left;
- elseif(ptr > node->ptr)
- p = &(*p)->rb_right;
- else
- returnNULL;
- }
- node = kzalloc(sizeof(*node), GFP_KERNEL);
- if(node == NULL)
- returnNULL;
- binder_stats.obj_created[BINDER_STAT_NODE]++;
- rb_link_node(&node->rb_node, parent, p);
- rb_insert_color(&node->rb_node, &proc->nodes);
- node->debug_id = ++binder_last_id;
- node->proc = proc;
- node->ptr = ptr;
- node->cookie = cookie;
- node->work.type = BINDER_WORK_NODE;
- INIT_LIST_HEAD(&node->work.entry);
- INIT_LIST_HEAD(&node->async_todo);
- if(binder_debug_mask & BINDER_DEBUG_INTERNAL_REFS)
- printk(KERN_INFO "binder: %d:%d node %d u%p c%p created\n",
- proc->pid, current->pid, node->debug_id,
- node->ptr, node->cookie);
- returnnode;
- }
注意,这里传进来的ptr和cookie均为NULL。函数首先检查proc->nodes红黑树中是否已经存在以ptr为键值的node,如果已经存在,就返回NULL。在这个场景下,由于当前线程是第一次进入到这里,所以肯定不存在,于是就新建了一个ptr为NULL的binder_node,并且初始化其它成员变量,并插入到proc->nodes红黑树中去。
binder_new_node返回到binder_ioctl函数后,就把新建的binder_node指针保存在binder_context_mgr_node中了,紧接着,又初始化了binder_context_mgr_node的引用计数值。
这样,BINDER_SET_CONTEXT_MGR命令就执行完毕了,binder_ioctl函数返回之前,执行了下面语句:
- if(thread)
- thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
回忆上面执行binder_get_thread时,thread->looper = BINDER_LOOPER_STATE_NEED_RETURN,执行了这条语句后,thread->looper = 0。
回到frameworks/base/cmds/servicemanager/service_manager.c文件中的main函数,下一步就是调用binder_loop函数进入循环,等待Client来请求了。binder_loop函数定义在frameworks/base/cmds/servicemanager/binder.c文件中:
- voidbinder_loop(structbinder_state *bs, binder_handler func)
- {
- intres;
- structbinder_write_read bwr;
- unsigned readbuf[32];
- bwr.write_size = 0;
- bwr.write_consumed = 0;
- bwr.write_buffer = 0;
- readbuf[0] = BC_ENTER_LOOPER;
- binder_write(bs, readbuf, sizeof(unsigned));
- for(;;) {
- bwr.read_size = sizeof(readbuf);
- bwr.read_consumed = 0;
- bwr.read_buffer = (unsigned) readbuf;
- res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
- if(res < 0) {
- LOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
- break;
- }
- res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
- if(res == 0) {
- LOGE("binder_loop: unexpected reply?!\n");
- break;
- }
- if(res < 0) {
- LOGE("binder_loop: io error %d %s\n", res, strerror(errno));
- break;
- }
- }
- }
首先是通过binder_write函数执行BC_ENTER_LOOPER命令告诉Binder驱动程序, Service Manager要进入循环了。
这里又要介绍一下设备文件/dev/binder文件操作函数ioctl的操作码BINDER_WRITE_READ了,首先看定义:
- #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
这个io操作码有一个参数,形式为struct binder_write_read:
- structbinder_write_read {
- signedlongwrite_size;/* bytes to write */
- signedlongwrite_consumed;/* bytes consumed by driver */
- unsigned longwrite_buffer;
- signedlongread_size;/* bytes to read */
- signedlongread_consumed;/* bytes consumed by driver */
- unsigned longread_buffer;
- };
这里顺便说一下,用户空间程序和Binder驱动程序交互大多数都是通过BINDER_WRITE_READ命令的,write_bufffer和read_buffer所指向的数据结构还指定了具体要执行的操作,write_bufffer和read_buffer所指向的结构体是struct binder_transaction_data:
- structbinder_transaction_data {
- /* The first two are only used for bcTRANSACTION and brTRANSACTION,
- * identifying the target and contents of the transaction.
- */
- union{
- size_thandle;/* target descriptor of command transaction */
- void*ptr;/* target descriptor of return transaction */
- } target;
- void*cookie;/* target object cookie */
- unsigned intcode;/* transaction command */
- /* General information about the transaction. */
- unsigned intflags;
- pid_t sender_pid;
- uid_t sender_euid;
- size_tdata_size;/* number of bytes of data */
- size_toffsets_size;/* number of bytes of offsets */
- /* If this transaction is inline, the data immediately
- * follows here; otherwise, it ends with a pointer to
- * the data buffer.
- */
- union{
- struct{
- /* transaction data */
- constvoid*buffer;
- /* offsets from buffer to flat_binder_object structs */
- constvoid*offsets;
- } ptr;
- uint8_t buf[8];
- } data;
- };
有一个联合体target,当这个BINDER_WRITE_READ命令的目标对象是本地Binder实体时,就使用ptr来表示这个对象在本进程中的地址,否则就使用handle来表示这个Binder实体的引用。只有目标对象是Binder实体时,cookie成员变量才有意义,表示一些附加数据,由Binder实体来解释这个个附加数据。code表示要对目标对象请求的命令代码,有很多请求代码,这里就不列举了,在这个场景中,就是BC_ENTER_LOOPER了,用来告诉Binder驱动程序, Service Manager要进入循环了。其余的请求命令代码可以参考kernel/common/drivers/staging/android/binder.h文件中定义的两个枚举类型BinderDriverReturnProtocol和BinderDriverCommandProtocol。
flags成员变量表示事务标志:
- enumtransaction_flags {
- TF_ONE_WAY = 0x01, /* this is a one-way call: async, no return */
- TF_ROOT_OBJECT = 0x04, /* contents are the component's root object */
- TF_STATUS_CODE = 0x08, /* contents are a 32-bit status code */
- TF_ACCEPT_FDS = 0x10, /* allow replies with file descriptors */
- };
每一个标志位所表示的意义看注释就行了,遇到时再具体分析。
sender_pid和sender_euid表示发送者进程的pid和euid。
data_size表示data.buffer缓冲区的大小,offsets_size表示data.offsets缓冲区的大小。这里需要解释一下data成员变量,命令的真正要传输的数据就保存在data.buffer缓冲区中,前面的一成员变量都是一些用来描述数据的特征的。data.buffer所表示的缓冲区数据分为两类,一类是普通数据,Binder驱动程序不关心,一类是Binder实体或者Binder引用,这需要Binder驱动程序介入处理。为什么呢?想想,如果一个进程A传递了一个Binder实体或Binder引用给进程B,那么,Binder驱动程序就需要介入维护这个Binder实体或者引用的引用计数,防止B进程还在使用这个Binder实体时,A却销毁这个实体,这样的话,B进程就会crash了。所以在传输数据时,如果数据中含有Binder实体和Binder引和,就需要告诉Binder驱动程序它们的具体位置,以便Binder驱动程序能够去维护它们。data.offsets的作用就在这里了,它指定在data.buffer缓冲区中,所有Binder实体或者引用的偏移位置。每一个Binder实体或者引用,通过struct flat_binder_object 来表示:
- /*
- * This is the flattened representation of a Binder object for transfer
- * between processes. The 'offsets' supplied as part of a binder transaction
- * contains offsets into the data where these structures occur. The Binder
- * driver takes care of re-writing the structure type and data as it moves
- * between processes.
- */
- structflat_binder_object {
- /* 8 bytes for large_flat_header. */
- unsigned longtype;
- unsigned longflags;
- /* 8 bytes of data. */
- union{
- void*binder;/* local object */
- signedlonghandle;/* remote object */
- };
- /* extra data associated with local object */
- void*cookie;
- };
type表示Binder对象的类型,它取值如下所示:
- enum{
- BINDER_TYPE_BINDER = B_PACK_CHARS('s','b','*', B_TYPE_LARGE),
- BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w','b','*', B_TYPE_LARGE),
- BINDER_TYPE_HANDLE = B_PACK_CHARS('s','h','*', B_TYPE_LARGE),
- BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w','h','*', B_TYPE_LARGE),
- BINDER_TYPE_FD = B_PACK_CHARS('f','d','*', B_TYPE_LARGE),
- };
flags表示Binder对象的标志,该域只对第一次传递Binder实体时有效,因为此刻驱动需要在内核中创建相应的实体节点,有些参数需要从该域取出。
type和flags的具体意义可以参考Android Binder设计与实现一文。
最后,binder表示这是一个Binder实体,handle表示这是一个Binder引用,当这是一个Binder实体时,cookie才有意义,表示附加数据,由进程自己解释。
数据结构分析完了,回到binder_loop函数中,首先是执行BC_ENTER_LOOPER命令:
- readbuf[0] = BC_ENTER_LOOPER;
- binder_write(bs, readbuf, sizeof(unsigned));
进入到binder_write函数中:
- intbinder_write(structbinder_state *bs,void*data, unsigned len)
- {
- structbinder_write_read bwr;
- intres;
- bwr.write_size = len;
- bwr.write_consumed = 0;
- bwr.write_buffer = (unsigned) data;
- bwr.read_size = 0;
- bwr.read_consumed = 0;
- bwr.read_buffer = 0;
- res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
- if(res < 0) {
- fprintf(stderr,"binder_write: ioctl failed (%s)\n",
- strerror(errno));
- }
- returnres;
- }
注意这里的binder_write_read变量bwr,write_size大小为4,表示write_buffer缓冲区大小为4,它的内容是一个BC_ENTER_LOOPER命令协议号,read_buffer为空。接着又是调用ioctl函数进入到Binder驱动程序的binder_ioctl函数,这里我们也只是关注BC_ENTER_LOOPER相关的逻辑:
- staticlongbinder_ioctl(structfile *filp, unsignedintcmd, unsignedlongarg)
- {
- intret;
- structbinder_proc *proc = filp->private_data;
- structbinder_thread *thread;
- unsigned intsize = _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);*/
- ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
- if(ret)
- returnret;
- mutex_lock(&binder_lock);
- thread= binder_get_thread(proc);
- if(thread== NULL) {
- ret = -ENOMEM;
- gotoerr;
- }
- switch(cmd) {
- caseBINDER_WRITE_READ: {
- structbinder_write_read bwr;
- if(size !=sizeof(structbinder_write_read)) {
- ret = -EINVAL;
- gotoerr;
- }
- if(copy_from_user(&bwr, ubuf,sizeof(bwr))) {
- ret = -EFAULT;
- gotoerr;
- }
- if(binder_debug_mask & BINDER_DEBUG_READ_WRITE)
- printk(KERN_INFO "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) {
- ret = binder_thread_write(proc, thread, (void__user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
- if(ret < 0) {
- bwr.read_consumed = 0;
- if(copy_to_user(ubuf, &bwr,sizeof(bwr)))
- ret = -EFAULT;
- gotoerr;
- }
- }
- 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);
- if(!list_empty(&proc->todo))
- wake_up_interruptible(&proc->wait);
- if(ret < 0) {
- if(copy_to_user(ubuf, &bwr,sizeof(bwr)))
- ret = -EFAULT;
- gotoerr;
- }
- }
- if(binder_debug_mask & BINDER_DEBUG_READ_WRITE)
- printk(KERN_INFO "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;
- gotoerr;
- }
- break;
- }
- ......
- default:
- ret = -EINVAL;
- gotoerr;
- }
- ret = 0;
- err:
- if(thread)
- thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
- mutex_unlock(&binder_lock);
- 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);
- returnret;
- }
函数前面的代码就不解释了,同前面调用binder_become_context_manager是一样的,只不过这里调用binder_get_thread函数获取binder_thread,就能从proc中直接找到了,不需要创建一个新的。
首先是通过copy_from_user(&bwr, ubuf, sizeof(bwr))语句把用户传递进来的参数转换成struct binder_write_read结构体,并保存在本地变量bwr中,这里可以看出bwr.write_size等于4,于是进入binder_thread_write函数,这里我们只关注BC_ENTER_LOOPER相关的代码:
- int
- binder_thread_write(structbinder_proc *proc,structbinder_thread *thread,
- void__user *buffer,intsize,signedlong*consumed)
- {
- uint32_t cmd;
- void__user *ptr = buffer + *consumed;
- void__user *end = buffer + size;
- while(ptr < end &&thread->return_error == BR_OK) {
- if(get_user(cmd, (uint32_t __user *)ptr))
- return-EFAULT;
- ptr += sizeof(uint32_t);
- if(_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
- binder_stats.bc[_IOC_NR(cmd)]++;
- proc->stats.bc[_IOC_NR(cmd)]++;
- thread->stats.bc[_IOC_NR(cmd)]++;
- }
- switch(cmd) {
- ......
- caseBC_ENTER_LOOPER:
- if(binder_debug_mask & BINDER_DEBUG_THREADS)
- printk(KERN_INFO "binder: %d:%d BC_ENTER_LOOPER\n",
- proc->pid, thread->pid);
- if(thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
- thread->looper |= BINDER_LOOPER_STATE_INVALID;
- binder_user_error("binder: %d:%d ERROR:"
- " BC_ENTER_LOOPER called after "
- "BC_REGISTER_LOOPER\n",
- proc->pid, thread->pid);
- }
- thread->looper |= BINDER_LOOPER_STATE_ENTERED;
- break;
- ......
- default:
- printk(KERN_ERR "binder: %d:%d unknown command %d\n", proc->pid,thread->pid, cmd);
- return-EINVAL;
- }
- *consumed = ptr - buffer;
- }
- return0;
- }
回忆前面执行binder_become_context_manager到binder_ioctl时,调用binder_get_thread函数创建的thread->looper值为0,所以这里执行完BC_ENTER_LOOPER时,thread->looper值就变为BINDER_LOOPER_STATE_ENTERED了,表明当前线程进入循环状态了。
回到binder_ioctl函数,由于bwr.read_size == 0,binder_thread_read函数就不会被执行了,这样,binder_ioctl的任务就完成了。
回到binder_loop函数,进入for循环:
- for(;;) {
- bwr.read_size = sizeof(readbuf);
- bwr.read_consumed = 0;
- bwr.read_buffer = (unsigned) readbuf;
- res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
- if(res < 0) {
- LOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
- break;
- }
- res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
- if(res == 0) {
- LOGE("binder_loop: unexpected reply?!\n");
- break;
- }
- if(res < 0) {
- LOGE("binder_loop: io error %d %s\n", res, strerror(errno));
- break;
- }
- }
又是执行一个ioctl命令,注意,这里的bwr参数各个成员的值:
- bwr.write_size = 0;
- bwr.write_consumed = 0;
- bwr.write_buffer = 0;
- readbuf[0] = BC_ENTER_LOOPER;
- bwr.read_size = sizeof(readbuf);
- bwr.read_consumed = 0;
- bwr.read_buffer = (unsigned) readbuf;
再次进入到binder_ioctl函数:
- staticlongbinder_ioctl(structfile *filp, unsignedintcmd, unsignedlongarg)
- {
- intret;
- structbinder_proc *proc = filp->private_data;
- structbinder_thread *thread;
- unsigned intsize = _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);*/
- ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
- if(ret)
- returnret;
- mutex_lock(&binder_lock);
- thread= binder_get_thread(proc);
- if(thread== NULL) {
- ret = -ENOMEM;
- gotoerr;
- }
- switch(cmd) {
- caseBINDER_WRITE_READ: {
- structbinder_write_read bwr;
- if(size !=sizeof(structbinder_write_read)) {
- ret = -EINVAL;
- gotoerr;
- }
- if(copy_from_user(&bwr, ubuf,sizeof(bwr))) {
- ret = -EFAULT;
- gotoerr;
- }
- if(binder_debug_mask & BINDER_DEBUG_READ_WRITE)
- printk(KERN_INFO "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) {
- ret = binder_thread_write(proc, thread, (void__user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
- if(ret < 0) {
- bwr.read_consumed = 0;
- if(copy_to_user(ubuf, &bwr,sizeof(bwr)))
- ret = -EFAULT;
- gotoerr;
- }
- }
- 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);
- if(!list_empty(&proc->todo))
- wake_up_interruptible(&proc->wait);
- if(ret < 0) {
- if(copy_to_user(ubuf, &bwr,sizeof(bwr)))
- ret = -EFAULT;
- gotoerr;
- }
- }
- if(binder_debug_mask & BINDER_DEBUG_READ_WRITE)
- printk(KERN_INFO "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;
- gotoerr;
- }
- break;
- }
- ......
- default:
- ret = -EINVAL;
- gotoerr;
- }
- ret = 0;
- err:
- if(thread)
- thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
- mutex_unlock(&binder_lock);
- 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);
- returnret;
- }
这次,bwr.write_size等于0,于是不会执行binder_thread_write函数,bwr.read_size等于32,于是进入到binder_thread_read函数:
- staticint
- binder_thread_read(structbinder_proc *proc,structbinder_thread *thread,
- void__user *buffer,intsize,signedlong*consumed,intnon_block)
- {
- void__user *ptr = buffer + *consumed;
- void__user *end = buffer + size;
- intret = 0;
- intwait_for_proc_work;
- if(*consumed == 0) {
- if(put_user(BR_NOOP, (uint32_t __user *)ptr))
- return-EFAULT;
- ptr += sizeof(uint32_t);
- }
- retry:
- wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);
- if(thread->return_error != BR_OK && ptr < end) {
- if(thread->return_error2 != BR_OK) {
- if(put_user(thread->return_error2, (uint32_t __user *)ptr))
- return-EFAULT;
- ptr += sizeof(uint32_t);
- if(ptr == end)
- gotodone;
- thread->return_error2 = BR_OK;
- }
- if(put_user(thread->return_error, (uint32_t __user *)ptr))
- return-EFAULT;
- ptr += sizeof(uint32_t);
- thread->return_error = BR_OK;
- gotodone;
- }
- thread->looper |= BINDER_LOOPER_STATE_WAITING;
- if(wait_for_proc_work)
- proc->ready_threads++;
- mutex_unlock(&binder_lock);
- if(wait_for_proc_work) {
- if(!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
- BINDER_LOOPER_STATE_ENTERED))) {
- binder_user_error("binder: %d:%d ERROR: Thread waiting "
- "for process work before calling BC_REGISTER_"
- "LOOPER or BC_ENTER_LOOPER (state %x)\n",
- proc->pid, thread->pid,thread->looper);
- wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
- }
- binder_set_nice(proc->default_priority);
- if(non_block) {
- if(!binder_has_proc_work(proc,thread))
- ret = -EAGAIN;
- } else
- ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));
- } else{
- if(non_block) {
- if(!binder_has_thread_work(thread))
- ret = -EAGAIN;
- } else
- ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));
- }
- .......
- }
传入的参数*consumed == 0,于是写入一个值BR_NOOP到参数ptr指向的缓冲区中去,即用户传进来的bwr.read_buffer缓冲区。这时候,thread->transaction_stack == NULL,并且thread->todo列表也是空的,这表示当前线程没有事务需要处理,于是wait_for_proc_work为true,表示要去查看proc是否有未处理的事务。当前thread->return_error == BR_OK,这是前面创建binder_thread时初始化设置的。于是继续往下执行,设置thread的状态为BINDER_LOOPER_STATE_WAITING,表示线程处于等待状态。调用binder_set_nice函数设置当前线程的优先级别为proc->default_priority,这是因为thread要去处理属于proc的事务,因此要将此thread的优先级别设置和proc一样。在这个场景中,proc也没有事务处理,即binder_has_proc_work(proc, thread)为false。如果文件打开模式为非阻塞模式,即non_block为true,那么函数就直接返回-EAGAIN,要求用户重新执行ioctl;否则的话,就通过当前线程就通过wait_event_interruptible_exclusive函数进入休眠状态,等待请求到来再唤醒了。
至此,我们就从源代码一步一步地分析完Service Manager是如何成为Android进程间通信(IPC)机制Binder守护进程的了。总结一下,Service Manager是成为Android进程间通信(IPC)机制Binder守护进程的过程是这样的:
1. 打开/dev/binder文件:open("/dev/binder", O_RDWR);
2. 建立128K内存映射:mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
3. 通知Binder驱动程序它是守护进程:binder_become_context_manager(bs);
4. 进入循环等待请求的到来:binder_loop(bs, svcmgr_handler);
在这个过程中,在Binder驱动程序中建立了一个struct binder_proc结构、一个struct binder_thread结构和一个struct binder_node结构,这样,Service Manager就在Android系统的进程间通信机制Binder担负起守护进程的职责了。
老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!
- 顶
- 65
- 踩
- 4
- 上一篇Android进程间通信(IPC)机制Binder简要介绍和学习计划
- 下一篇浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路
我的同类文章
- •Chromium扩展(Extension)的页面(Page)加载过程分析2016-09-19
- •Chromium扩展(Extension)机制简要介绍和学习计划2016-09-05
- •Chromium为视频标签<video>渲染视频画面的过程分析2016-08-22
- •Chromium视频标签<video>简要介绍和学习计划2016-08-08
- •Chromium网页滑动和捏合手势处理过程分析2016-07-11
- •Chromium网页输入事件处理机制简要介绍和学习计划2016-06-27
- •Chromium扩展(Extension)加载过程分析2016-09-12
- •Chromium为视频标签<video>全屏播放的过程分析2016-08-29
- •Chromium为视频标签<video>创建播放器的过程分析2016-08-15
- •Chromium分发输入事件给WebKit处理的过程分析2016-07-25
- •Chromium网页输入事件捕捉和手势检测过程分析2016-07-04
更多文章
参考知识库
- 猜你在找
- C/C++高级开发实战 快速掌握C/C++ 适合iOS/Android/Linux
- Android驱动深度开发视频教程
- 公开课:TCP协议的可靠性传输机制
- ArcGIS for javascript 项目实战(环境监测系统)
- ArcGIS for JavaScript
- 浅谈Service Manager成为Android进程间通信IPC机制Binder守护进程之路
- 浅谈Service Manager成为Android进程间通信IPC机制Binder守护进程之路
- 浅谈Service Manager成为Android进程间通信IPC机制Binder守护进程之路
- 浅谈Service Manager成为Android进程间通信IPC机制Binder守护进程之路
- 浅谈Service Manager成为Android进程间通信IPC机制Binder守护进程之路
浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路相关推荐
- 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路(1)
上一篇文章Android进程间通信(IPC)机制Binder简要介绍和学习计划简要介绍了Android系统进程间通信机制Binder的总体架构,它由Client.Server.Service Mana ...
- 深入理解 Android 的 IPC 机制--------Binder
在android中,,应用程序虽然是以独立的进程来运行的,但相互之间还是需要通信..比如,,我们的应用程序 和后台服务往往会运行不同的进程当中,,各自有这独立内存地址空间,,,但是又要彼此互相合作,, ...
- Android的IPC机制Binder
第一部分 Binder的组成 1.1 驱动程序部分驱动程序的部分在以下的文件夹中: Java代码 kernel/include/linux/binder.h kernel/drivers/andr ...
- 浅谈 unix, linux, ios, android 区别和联系
浅谈 unix, linux, ios, android 区别和联系 网上的答案并不是很好,便从网上整理的相对专业的问答. 1.UNIX 和 Linux UNIX 操作系统(尤尼斯), 是一个强大的多 ...
- Android进程间通信之一:Binder机制学习
Binder机制学习 Binder驱动 Binder核心API Linux 使用两级保护机制:0 级供系统内核使用,3 级供用户程序使用. Linux 下的传统 IPC 通信原理 Linux 下的传统 ...
- 多线程之旅之四——浅谈内存模型和用户态同步机制
用户态下有两种同步结构的 volatile construct: 在简单数据类型上原子性的读或者写操作 interlocked construct:在简单数据类型上原子性的读和写操作 (在这里还 ...
- 浅谈专有云MQ存储空间的清理机制
简介:浅谈专有云MQ存储空间的清理机制 在近⼀年的项⽬保障过程中,对专有云MQ产品的存储⽔位清理模式⼀直存疑,总想一探究竟但又苦于工作繁忙.精力有限,直到最近⼀次项⽬保障过程中再次出现了类似的问题,⼤ ...
- 浅谈V8引擎中的垃圾回收机制
浅谈V8引擎中的垃圾回收机制 这篇文章的所有内容均来自 朴灵的<深入浅出Node.js>及A tour of V8:Garbage Collection,后者还有中文翻译版V8 之旅: 垃 ...
- 浅谈实时对战网络游戏的同步机制
浅谈实时对战网络游戏的同步机制 重要的性能指标 三种不同方向的技术实现介绍 非帧状态同步 帧指令同步 帧状态同步 三种同步方式的对比 帧状态同步和ECS架构 实时对战游戏,相信大家都不陌生,一些经典的 ...
最新文章
- 解决 The mysql extension is deprecated and will be r
- WebView的截屏实现
- TensorFlow 笔记2--MNIST手写数字分类
- Redis开发:hash存储自定义Java对象及value的序列化器设置
- 关于mysql中外键关联的一些个人理解
- 数据库的几个概念:主键,外键,索引,唯一索引
- NameError: name 'long' is not defined
- Java LinkedList公共布尔boolean offerLast(Object o)方法(带示例)
- 弹性理论法研究桩基受力计算公式_竖向荷载下群桩的承载力分析
- 计算机网络课程设计之简单 Web Server 程序的设计与实现
- org.xml.sax.SAXParseException: Failed to read schema document错误的完美解决方法 以及 Spring如何加载XSD文件
- java varargs_Java中方法重载中的Varargs
- C#控制台程序取得INSOYA视频区的视频的真实URL,视频标题,发布时间集合。
- activity绑定service
- qt视频教程qt入门教程嵌入式linux应用开发qt编程开发,QT项目实战教程-Qt应用项目实例开发视频教程...
- 修复win10右键卡死鼠标转圈
- 公司-瑞幸咖啡:瑞幸咖啡
- 理工附中2021年高考成绩查询,人大附中、理工附中、101中学、十二中等5区10校高考成绩汇总!...
- JavaWeb购物系统
- js ios调用ios方法_通过iOS 13的模式演示调用生命周期方法
热门文章
- [最新答案V0.4版]微软等数据结构+算法面试100题[第41-60题答案]
- c++学习笔记之类和对象的进阶
- Python科学画图小结
- 超强包管理器:Anaconda 安装
- 用C语言扩展Python的功能的实例
- 量子遗传算法原理与MATLAB仿真程序
- 二十万字C/C++、嵌入式软开面试题全集宝典八
- 科大星云诗社动态20210202
- 这 28 张精炼图,将吴恩达的 deeplearning.ai 总结得恰到好处!
- 吴恩达《卷积神经网络》精炼笔记(3)-- 目标检测
守护进程不是要六七步才能成为吗,为什么没有相关成为守护进程的代码?
请教一个问题:binder的权限是proc是否拥有对binder IPC操作权限(Binder IPC:"impersonate", "call", "set_context_mgr", "transfer"),如何禁止某一个client访问某一个server,具体策略是如何定制的?
请教一个问题:binder的权限是proc是否拥有对binder IPC操作权限(Binder IPC:"impersonate", "call", "set_context_mgr", "transfer"),如何禁止某一个client访问某一个server,具体策略是如何定制的?
while (*p) {
parent = *p;
node = rb_entry(parent, struct binder_node, rb_node);
if (ptr < node->ptr)
p = &(*p)->rb_left;
else if (ptr > node->ptr)
p = &(*p)->rb_right;
else
return NULL;
}
这里为何最后要返回NULL?按照正常理解,找到这个节点node后,就应该return node。
http://blog.csdn.net/happy08god/article/details/18730461
当我阅读了LZ的文章后,觉得自己跟您的差距实在太大了。看来学习的路还很长啊!!
@上面原文:“用户空间程序和Binder驱动程序交互大多数都是通过BINDER_WRITE_READ命令的,write_bufffer和read_buffer所指向的数据结构还指定了具体要执行的操作,write_bufffer和read_buffer所指向的结构体是struct binder_transaction_data:”
但在serveice_manger.c 的 函数binder_loop(struct binder_state *bs, binder_handler func)
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (unsigned) readbuf;
这里面的write_bufffer和read_buffer都没用到结构体是struct binder_transaction_data。?
问题:write_buffer和reader_buffer的数据格式必须按照结构体是struct binder_transaction_data组织数据?
还是说是在某些情况下使用?
thread->pid = current->pid
current->pid是进程的pid,这里怎么赋值给了线程pid?
那是不是mmap()系统调用的行为由各自驱动程序定义的,也可以在binder_mmap()函数中做其他事情,如这里就分配了物理页面并映射到内核空间和用户空间,从而调用进程操作的实际上是分配的物理页面啊?
如果你研究一下Binder驱动的代码,就会发现虽然/dev/binder虽然是一个字符类型的设备文件,但是它不提供有读写操作,即不能对它进行read和write操作,而且用户空间程序对它进行内存映射的时候,只提供了读访问权限。也就是说,对/dev/binder这个设备文件,你只能先将它映射到内存,然后再读取它的内容,但是不能写它的内容。
那是不是可以这样理解这种映射:
SM通过binder驱动申请了一块128*1024大小的物理内存,内核地址空间也会映射上这块内存,SM的client端要想与SM通信,就指明binder驱动我需要0号引用的东西,驱动会操作内核中映射的地址空间,从而就影响到SM中的物理内存?
对这里面的关系还是云里雾里的。
1. Client将数据从用户空间传输到Binder驱动;
2. Binder驱动将第1步得到的数据拷贝到Service通过mmap申请得到的那块物理空间;
3. Binder驱动将第2步得到的物理空间对应的虚拟地址传递给Service的用户空间;
4. Service的用户空间通过Binder驱动传递过来的虚拟地址来访问Client传输过来的数据。
整个过程只有第2步是需要拷贝数据的,这也是Binder进程间通信机制的精华所在。
在Binder IPC执行期间,Server可以通过Binder.getCallingPid和Binder.getCallingUid来获得Client的PID和UID。
在非Binder IPC执行期间,Server可以通过Binder.getCallingPid和Binder.getCallingUid获得的是自己的PID和UID。
在Binder IPC执行期间,有些特殊操作是需要以Server的身份来执行的,这时候调用Binder.clearCallingIdentity就可以使得调用Binder.getCallingPid和Binder.getCallingUid获得的是Server自己的PID和UID。
这些特殊操作执行完成后,需要调用Binder.restoreCallingIdentity当前的运行上下文为Client的PID和UID,即使得Binder.getCallingPid和Binder.getCallingUid的返回值为Client的PID和UID。
01.static struct binder_thread *binder_get_thread(struct binder_proc *proc)
02.{
03. struct binder_thread *thread = NULL;
04. struct rb_node *parent = NULL;
05. struct rb_node **p = &proc->threads.rb_node;
这里为什么不弄成struct rb-node *p = proc->threads.rb_node呢?
请问一下,我在log中发现这样的信息:
binder: 1194: binder_alloc_buf, no vma
这一般是什么原因造成的?
详细的一段log是这样的:
binder: 1194: binder_alloc_buf, no vma
binder: 287:594 transaction failed 29201, size 164-0
binder: 1194: binder_alloc_buf, no vma
binder: 287:504 transaction failed 29201, size 60-0
进程1194是一个CMMB视频播放器,进程287是PowerManagerService,后面手机会重启。
是因为CMMB的内存使用有问题吗,/dev/binder被umap可能是什么原因导致的?
12-18 18:31:48.580 248 388 E JavaBinder: !!! FAILED BINDER TRANSACTION !!!
12-18 18:31:48.580 248 391 I WindowManager: WINDOW DIED Window{40a15eb0 com.youku.pad/com.youku.pad.TabBarActivity paused=true}
12-18 18:31:48.580 248 391 D PowerManagerService: setScreenBrightnessOverride -1
12-18 18:31:48.580 248 391 D PowerManagerService: setButtonBrightnessOverride -1
<3>[ 3321.124000] binder: 736: binder_alloc_buf, no vma
<6>[ 3321.124000] binder: 248:388 transaction failed 29201, size 60-0
此处进程736是一个在线视频播放器,出现的问题现象是视频播放卡住,然后重启。
bs->fd = open("/dev/binder", O_RDWR);这个语句是属于用户态下执行的,它调用open这个函数,也即binder_open驱动函数,是属于内核态的代码。编译serveice_manange这个独立程序的时候肯定不会把binder_open这个驱动函数编译进去的。请教,serveice_manange是如何调用到binder_open这个驱动函数de?
2. open函数是一个系统调用,在执行系统调用的时候,CPU会从用户态切换为内核态,等到内核态的binder_open执行完成之后,再返回到用户态。
这个知识点需要了解一下操作系统方面的知识^_^
一、BnServiceManager在OnTransact函数中调用addService函数,但是在service_manager文件中是do_add_service函数,它们怎么对应起来的啊?
二、BpMediaPlayerService在调用transact时,后来怎么转成了BnMediaPlayerService调用OnTransact函数了,是不是在驱动中维持了引用与本地对象的映射关系,对于驱动来说没有进程一说,都是一样的?remote()函数返回的是BpBinder对象,然后它调用transact函数,到内核中怎么变成BBinder调用transact再调OnTransact的啊?对应的代码我没找到,可能没看明白。
第二个问题是这样的:BnMediaPlayerService是一个Binder本地对象,它在内核中有一个对应的Binder实体对象,当另外一个进程要使用MediaPlayerService的时候,Binder驱动程序就会在内核创建一个Binder引用对象,同时在那个进程创建一个Binder代理对象,即一个BpMediaPlayerService对象。这四个对象是有一一对应关系的,因此,当一个进程调用BpMediaPlayerService对象的transact函数时,通过Binder驱动程序就可以找到对应的BnMediaPlayerService对象,并且调用它的OnTransact函数。这一点在我即将要出版的书中将会有非常详细的描述。
对于第一个问题,我很想知道addService怎么调用到do_add_service的,驱动中有实现吗?
第二个问题我也知道是一一对应的,是不是在红黑树中保存了对应的关系呢?内核中的binder引用和内核中的binder实体在驱动中怎么做到的啊,这段驱动看不太懂,不知是否有详细介绍,在你的书中?回复Luoshengyang:
第二个问题在我的书里面都会有讲述。为了讲清楚Binder机制,书里面花了有200页的篇幅。你可以看一下这篇文章:http://blog.csdn.net/luoshengyang/article/details/7409491,这里有详细的目录。
-----------------
addr + user_buffer_offset 这个是否写错了?
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer
vma->vm_start是用户空间地址,而proc->buffer是内核空间地址,proc->user_buffer_offset是差值,换一下就是:
vma->vm_start = (uintptr_t)proc->buffer + proc->user_buffer_offset
即:
用户空间地址 = 内核空间地址 + proc->user_buffer_offset
上面提到的addr是一个内核空间址。
proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);如果vma->vm_end - vma->vm_start小于PAGE_SIZE,即长度不到一页,那这里proc->pages == NULL是不是就失败了呢。或是vma->vm_end - vma->vm_start不是 PAGE_SIZE的整数倍,(vma->vm_end - vma)% PAGE_SIZE的字节不是没有被分配吗?
博主,驱动 mmap 的实现里面有太多的疑惑了。主要在binder_update_page_range这个函数里面
1.proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
BUG_ON(*page);
。。。。。。。。。。。
binder_update_page_range
*page = alloc_page(GFP_KERNEL | __GFP_ZERO);
这里的page 在 mmap里面已经申请内存了,在这个for循环里是不是不会在申请内存了?
2.ret = vm_insert_page(vma, user_page_addr, page[0]);
vma是 用户空间mmap时传递的,大小应该是BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
但是一个 struct page 结构体只能描述一页的大小, 是不是说只为 用户的虚拟空间 和 内核 的虚拟空间 都只映射了一页的物理内存?
3.如果上面说的对,那门如果用户要使用mmap 返回的虚拟空间,大于一页是不是会出问题?因为 在内核中对这个vwa结构体的操作函数中,并没有实现缺也异常处理。
static struct vm_operations_struct binder_vm_ops = {
.open = binder_vma_open,
.close = binder_vma_close,
};
3. 为什么之映射一页的空间?上面的交互过程中并没有看见要使用这个映射的虚拟地址。
4.proc->buffer 这个内核虚拟空间是 binder_buffer结构体来 管理描述的。怎么来管理描述?前面分配的那一页内存怎么描述的?
5.binder_node binder_thread 等等太多的结构体了,我实在里不清楚关系了,希望博主帮忙啊。
还希望博主多分享些学习的经验啊
2. 不是,会根据需要来分配更多的物理页面,只是一开始的时候只分配一个;
3. 参见2的答案;
4和5一言难尽~合适的时候我会把Binder驱动程序用到的数据结构都详细介绍一遍。
这个函数没搞明白,vma要映射的空间很大,为什么值传递了 一个page?
因为struct page 结构体是用来描述一个物理页的,所以上面的作用是不是把page 结构体描述的那个 物理页面 和 vma描述的 虚拟空间 对应起来?
这个区域是没有物理页面的,所以驱动有两种方法来分配页框,一种是全部分配,一种是一页一页的分,但是这种分法需要提供缺页异常函数啊。但是这里的mmap并没看到缺页异常的函数在哪?
以后用到时会分配?怎么分,调用什么函数分?这个mmap返回的地址就没见在哪里使用过。博主能不能说一下在哪里用到了这个映射?
#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
所以理论上传200K的图片是没有问题的。这个问题我觉得最好是看一下"out of memory"这个log是在哪里输出来的,然后再看一下到底是哪里出了问题。Intent的最终会通过它的writeToParcel成员函数把数据都写到一个Parcel里面去,然后再传输,可以沿着这条路分析下去。
贊
以mediaplayerservice为例,IMediaPlayerClient有个callback函数,notify(int msg, int ext1, int ext2),mediaplayerclient get mediaplayer service以及其他的setup过程中,会把this指针以IBinder的类型注册给server,而server(mediaplayerservice)则可以通过这个IBinder notify callback回去mediaplayerclient,这里是sever主动callback client,其实client已经担当server的角色,而server在担当client的角色,通常来说mediaplayerclient应该是跑在java进程里,native的service能找到jointhreadpool的调用,这里下了BC_ENTER_LOOPER告诉driver进入循环。对应来说,java的主线程或者其它地方是否也应该有同样的操作,否则,client如何知道什么时候有notify的数据?
Java层已经为我们封装好了一切,当我们在启动一个Android应用程序的时候,一般会通过Process.start接口启动一个新的进程,新的进程在启动的时候,会通过JNI层,最覆盖调用运行时库的ProcessState::startThreadPool函数启动一个线程池,线程池里面的线程最终都会进入到IPCThreadState::joinThreadPool函数中,这个函数会有一个无限循环在和Binder驱动程序进行交互,直到线程退出。
因此,在Java层,我们只要实现自己的Binder对象就可以了,其它的进程只要拿到这个Binder对象的远程接口,就可以调用它;但是如果我们在C++层实现自己的Binder对象,就要自己手动地调用ProcessState::startThreadPool来进入一个无限循环来和Binder驱动程序交互,以便获得Client的请求。
有时间的话,我再写一篇文章来描述一下这个过程。
为什么会对应Binder驱动程序的binder_mmap函数?
new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1;
for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
ref = rb_entry(n, struct binder_ref, rb_node_desc);
if (ref->desc > new_ref->desc)
break;
new_ref->desc = ref->desc + 1;
}
如果引用Binder实体不是binder_context_mgr_node,就会从1开始,在proc->refs_by_desc树里面,找到一个当前没有使用的句柄值来作为新的handle值