原文地址: 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函数:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. intmain(intargc,char**argv)
  2. {
  3. structbinder_state *bs;
  4. void*svcmgr = BINDER_SERVICE_MANAGER;
  5. bs = binder_open(128*1024);
  6. if(binder_become_context_manager(bs)) {
  7. LOGE("cannot become context manager (%s)\n", strerror(errno));
  8. return-1;
  9. }
  10. svcmgr_handle = svcmgr;
  11. binder_loop(bs, svcmgr_handler);
  12. return0;
  13. }

main函数主要有三个功能:一是打开Binder设备文件;二是告诉Binder驱动程序自己是Binder上下文管理者,即我们前面所说的守护进程;三是进入一个无穷循环,充当Server的角色,等待Client的请求。进入这三个功能之间,先来看一下这里用到的结构体binder_state、宏BINDER_SERVICE_MANAGER的定义:

struct binder_state定义在frameworks/base/cmds/servicemanager/binder.c文件中:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. structbinder_state
  2. {
  3. intfd;
  4. void*mapped;
  5. unsigned mapsize;
  6. };

fd是文件描述符,即表示打开的/dev/binder设备文件描述符;mapped是把设备文件/dev/binder映射到进程空间的起始地址;mapsize是上述内存映射空间的大小。

宏BINDER_SERVICE_MANAGER定义frameworks/base/cmds/servicemanager/binder.h文件中:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. /* the one magic object */
  2. #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文件中:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. structbinder_state *binder_open(unsigned mapsize)
  2. {
  3. structbinder_state *bs;
  4. bs = malloc(sizeof(*bs));
  5. if(!bs) {
  6. errno = ENOMEM;
  7. return0;
  8. }
  9. bs->fd = open("/dev/binder", O_RDWR);
  10. if(bs->fd < 0) {
  11. fprintf(stderr,"binder: cannot open device (%s)\n",
  12. strerror(errno));
  13. gotofail_open;
  14. }
  15. bs->mapsize = mapsize;
  16. bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
  17. if(bs->mapped == MAP_FAILED) {
  18. fprintf(stderr,"binder: cannot map device (%s)\n",
  19. strerror(errno));
  20. gotofail_map;
  21. }
  22. /* TODO: check version */
  23. returnbs;
  24. fail_map:
  25. close(bs->fd);
  26. fail_open:
  27. free(bs);
  28. return0;
  29. }

通过文件操作函数open来打开/dev/binder设备文件。设备文件/dev/binder是在Binder驱动程序模块初始化的时候创建的,我们先看一下这个设备文件的创建过程。进入到kernel/common/drivers/staging/android目录中,打开binder.c文件,可以看到模块初始化入口binder_init:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. staticstructfile_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. };
  10. staticstructmiscdevice binder_miscdev = {
  11. .minor = MISC_DYNAMIC_MINOR,
  12. .name = "binder",
  13. .fops = &binder_fops
  14. };
  15. staticint__init binder_init(void)
  16. {
  17. intret;
  18. binder_proc_dir_entry_root = proc_mkdir("binder", NULL);
  19. if(binder_proc_dir_entry_root)
  20. binder_proc_dir_entry_proc = proc_mkdir("proc", binder_proc_dir_entry_root);
  21. ret = misc_register(&binder_miscdev);
  22. if(binder_proc_dir_entry_root) {
  23. create_proc_read_entry("state", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_state, NULL);
  24. create_proc_read_entry("stats", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_stats, NULL);
  25. create_proc_read_entry("transactions", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_transactions, NULL);
  26. create_proc_read_entry("transaction_log", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_transaction_log, &binder_transaction_log);
  27. create_proc_read_entry("failed_transaction_log", S_IRUGO, binder_proc_dir_entry_root, binder_read_proc_transaction_log, &binder_transaction_log_failed);
  28. }
  29. returnret;
  30. }
  31. device_initcall(binder_init);

创建设备文件的地方在misc_register函数里面,关于misc设备的注册,我们在Android日志系统驱动程序Logger源代码分析一文中有提到,有兴趣的读取不访去了解一下。其余的逻辑主要是在/proc目录创建各种Binder相关的文件,供用户访问。从设备文件的操作方法binder_fops可以看出,前面的binder_open函数执行语句:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. bs->fd = open("/dev/binder", O_RDWR);

就进入到Binder驱动程序的binder_open函数了:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. staticintbinder_open(structinode *nodp,structfile *filp)
  2. {
  3. structbinder_proc *proc;
  4. if(binder_debug_mask & BINDER_DEBUG_OPEN_CLOSE)
  5. printk(KERN_INFO "binder_open: %d:%d\n", current->group_leader->pid, current->pid);
  6. proc = kzalloc(sizeof(*proc), GFP_KERNEL);
  7. if(proc == NULL)
  8. return-ENOMEM;
  9. get_task_struct(current);
  10. proc->tsk = current;
  11. INIT_LIST_HEAD(&proc->todo);
  12. init_waitqueue_head(&proc->wait);
  13. proc->default_priority = task_nice(current);
  14. mutex_lock(&binder_lock);
  15. binder_stats.obj_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. filp->private_data = proc;
  20. mutex_unlock(&binder_lock);
  21. if(binder_proc_dir_entry_proc) {
  22. charstrbuf[11];
  23. snprintf(strbuf, sizeof(strbuf),"%u", proc->pid);
  24. remove_proc_entry(strbuf, binder_proc_dir_entry_proc);
  25. create_proc_read_entry(strbuf, S_IRUGO, binder_proc_dir_entry_proc, binder_read_proc_proc, proc);
  26. }
  27. return0;
  28. }

这个函数的主要作用是创建一个struct binder_proc数据结构来保存打开设备文件/dev/binder的进程的上下文信息,并且将这个进程上下文信息保存在打开文件结构struct file的私有数据成员变量private_data中,这样,在执行其它文件操作时,就通过打开文件结构struct file来取回这个进程上下文信息了。这个进程上下文信息同时还会保存在一个全局哈希表binder_procs中,驱动程序内部使用。binder_procs定义在文件的开头:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. staticHLIST_HEAD(binder_procs);

结构体struct binder_proc也是定义在kernel/common/drivers/staging/android/binder.c文件中:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. structbinder_proc {
  2. structhlist_node proc_node;
  3. structrb_root threads;
  4. structrb_root nodes;
  5. structrb_root refs_by_desc;
  6. structrb_root refs_by_node;
  7. intpid;
  8. structvm_area_struct *vma;
  9. structtask_struct *tsk;
  10. structfiles_struct *files;
  11. structhlist_node deferred_work_node;
  12. intdeferred_work;
  13. void*buffer;
  14. ptrdiff_tuser_buffer_offset;
  15. structlist_head buffers;
  16. structrb_root free_buffers;
  17. structrb_root allocated_buffers;
  18. size_tfree_async_space;
  19. structpage **pages;
  20. size_tbuffer_size;
  21. uint32_t buffer_free;
  22. structlist_head todo;
  23. wait_queue_head_t wait;
  24. structbinder_stats stats;
  25. structlist_head delivered_death;
  26. intmax_threads;
  27. intrequested_threads;
  28. intrequested_threads_started;
  29. intready_threads;
  30. longdefault_priority;
  31. };

这个结构体的成员比较多,这里就不一一说明了,简单解释一下四个成员变量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:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);

对应Binder驱动程序的binder_mmap函数:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. staticintbinder_mmap(structfile *filp,structvm_area_struct *vma)
  2. {
  3. intret;
  4. structvm_struct *area;
  5. structbinder_proc *proc = filp->private_data;
  6. constchar*failure_string;
  7. structbinder_buffer *buffer;
  8. if((vma->vm_end - vma->vm_start) > SZ_4M)
  9. vma->vm_end = vma->vm_start + SZ_4M;
  10. if(binder_debug_mask & BINDER_DEBUG_OPEN_CLOSE)
  11. printk(KERN_INFO
  12. "binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n",
  13. proc->pid, vma->vm_start, vma->vm_end,
  14. (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
  15. (unsigned long)pgprot_val(vma->vm_page_prot));
  16. if(vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
  17. ret = -EPERM;
  18. failure_string = "bad vm_flags";
  19. gotoerr_bad_arg;
  20. }
  21. vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
  22. if(proc->buffer) {
  23. ret = -EBUSY;
  24. failure_string = "already mapped";
  25. gotoerr_already_mapped;
  26. }
  27. area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
  28. if(area == NULL) {
  29. ret = -ENOMEM;
  30. failure_string = "get_vm_area";
  31. gotoerr_get_vm_area_failed;
  32. }
  33. proc->buffer = area->addr;
  34. proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
  35. #ifdef CONFIG_CPU_CACHE_VIPT
  36. if(cache_is_vipt_aliasing()) {
  37. while(CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
  38. printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
  39. vma->vm_start += PAGE_SIZE;
  40. }
  41. }
  42. #endif
  43. proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
  44. if(proc->pages == NULL) {
  45. ret = -ENOMEM;
  46. failure_string = "alloc page array";
  47. gotoerr_alloc_pages_failed;
  48. }
  49. proc->buffer_size = vma->vm_end - vma->vm_start;
  50. vma->vm_ops = &binder_vm_ops;
  51. vma->vm_private_data = proc;
  52. if(binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
  53. ret = -ENOMEM;
  54. failure_string = "alloc small buf";
  55. gotoerr_alloc_small_buf_failed;
  56. }
  57. buffer = proc->buffer;
  58. INIT_LIST_HEAD(&proc->buffers);
  59. list_add(&buffer->entry, &proc->buffers);
  60. buffer->free = 1;
  61. binder_insert_free_buffer(proc, buffer);
  62. proc->free_async_space = proc->buffer_size / 2;
  63. barrier();
  64. proc->files = get_files_struct(current);
  65. proc->vma = vma;
  66. /*printk(KERN_INFO "binder_mmap: %d %lx-%lx maps %p\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/
  67. return0;
  68. err_alloc_small_buf_failed:
  69. kfree(proc->pages);
  70. proc->pages = NULL;
  71. err_alloc_pages_failed:
  72. vfree(proc->buffer);
  73. proc->buffer = NULL;
  74. err_get_vm_area_failed:
  75. err_already_mapped:
  76. err_bad_arg:
  77. printk(KERN_ERR "binder_mmap: %d %lx-%lx %s failed %d\n", proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
  78. returnret;
  79. }

函数首先通过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来描述:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. structbinder_buffer {
  2. structlist_head entry;/* free and allocated entries by addesss */
  3. structrb_node rb_node;/* free entry by size or allocated entry */
  4. /* by address */
  5. unsigned free : 1;
  6. unsigned allow_user_free : 1;
  7. unsigned async_transaction : 1;
  8. unsigned debug_id : 29;
  9. structbinder_transaction *transaction;
  10. structbinder_node *target_node;
  11. size_tdata_size;
  12. size_toffsets_size;
  13. uint8_t data[0];
  14. };

每一个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驱动程序是如何实现把一个物理页面同时映射到内核空间和进程空间去的:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. staticintbinder_update_page_range(structbinder_proc *proc,intallocate,
  2. void*start,void*end,structvm_area_struct *vma)
  3. {
  4. void*page_addr;
  5. unsigned longuser_page_addr;
  6. structvm_struct tmp_area;
  7. structpage **page;
  8. structmm_struct *mm;
  9. if(binder_debug_mask & BINDER_DEBUG_BUFFER_ALLOC)
  10. printk(KERN_INFO "binder: %d: %s pages %p-%p\n",
  11. proc->pid, allocate ? "allocate":"free", start, end);
  12. if(end <= start)
  13. return0;
  14. if(vma)
  15. mm = NULL;
  16. else
  17. mm = get_task_mm(proc->tsk);
  18. if(mm) {
  19. down_write(&mm->mmap_sem);
  20. vma = proc->vma;
  21. }
  22. if(allocate == 0)
  23. gotofree_range;
  24. if(vma == NULL) {
  25. printk(KERN_ERR "binder: %d: binder_alloc_buf failed to "
  26. "map pages in userspace, no vma\n", proc->pid);
  27. gotoerr_no_vma;
  28. }
  29. for(page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
  30. intret;
  31. structpage **page_array_ptr;
  32. page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
  33. BUG_ON(*page);
  34. *page = alloc_page(GFP_KERNEL | __GFP_ZERO);
  35. if(*page == NULL) {
  36. printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
  37. "for page at %p\n", proc->pid, page_addr);
  38. gotoerr_alloc_page_failed;
  39. }
  40. tmp_area.addr = page_addr;
  41. tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;
  42. page_array_ptr = page;
  43. ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);
  44. if(ret) {
  45. printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
  46. "to map page at %p in kernel\n",
  47. proc->pid, page_addr);
  48. gotoerr_map_kernel_failed;
  49. }
  50. user_page_addr =
  51. (uintptr_t)page_addr + proc->user_buffer_offset;
  52. ret = vm_insert_page(vma, user_page_addr, page[0]);
  53. if(ret) {
  54. printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
  55. "to map page at %lx in userspace\n",
  56. proc->pid, user_page_addr);
  57. gotoerr_vm_insert_page_failed;
  58. }
  59. /* vm_insert_page does not seem to increment the refcount */
  60. }
  61. if(mm) {
  62. up_write(&mm->mmap_sem);
  63. mmput(mm);
  64. }
  65. return0;
  66. free_range:
  67. for(page_addr = end - PAGE_SIZE; page_addr >= start;
  68. page_addr -= PAGE_SIZE) {
  69. page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
  70. if(vma)
  71. zap_page_range(vma, (uintptr_t)page_addr +
  72. proc->user_buffer_offset, PAGE_SIZE, NULL);
  73. err_vm_insert_page_failed:
  74. unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
  75. err_map_kernel_failed:
  76. __free_page(*page);
  77. *page = NULL;
  78. err_alloc_page_failed:
  79. ;
  80. }
  81. err_no_vma:
  82. if(mm) {
  83. up_write(&mm->mmap_sem);
  84. mmput(mm);
  85. }
  86. return-ENOMEM;
  87. }

这个函数既可以分配物理页面,也可以用来释放物理页面,通过allocate参数来区别,这里我们只关注分配物理页面的情况。要分配物理页面的虚拟地址空间范围为(start ~ end),函数前面的一些检查逻辑就不看了,直接看中间的for循环:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. for(page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
  2. intret;
  3. structpage **page_array_ptr;
  4. page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
  5. BUG_ON(*page);
  6. *page = alloc_page(GFP_KERNEL | __GFP_ZERO);
  7. if(*page == NULL) {
  8. printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
  9. "for page at %p\n", proc->pid, page_addr);
  10. gotoerr_alloc_page_failed;
  11. }
  12. tmp_area.addr = page_addr;
  13. tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;
  14. page_array_ptr = page;
  15. ret = map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);
  16. if(ret) {
  17. printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
  18. "to map page at %p in kernel\n",
  19. proc->pid, page_addr);
  20. gotoerr_map_kernel_failed;
  21. }
  22. user_page_addr =
  23. (uintptr_t)page_addr + proc->user_buffer_offset;
  24. ret = vm_insert_page(vma, user_page_addr, page[0]);
  25. if(ret) {
  26. printk(KERN_ERR "binder: %d: binder_alloc_buf failed "
  27. "to map page at %lx in userspace\n",
  28. proc->pid, user_page_addr);
  29. gotoerr_vm_insert_page_failed;
  30. }
  31. /* vm_insert_page does not seem to increment the refcount */
  32. }

首先是调用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文件中:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. intbinder_become_context_manager(structbinder_state *bs)
  2. {
  3. returnioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
  4. }

这里通过调用ioctl文件操作函数来通知Binder驱动程序自己是守护进程,命令号是BINDER_SET_CONTEXT_MGR,没有参数。BINDER_SET_CONTEXT_MGR定义为:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. #define BINDER_SET_CONTEXT_MGR      _IOW('b', 7, int)

这样就进入到Binder驱动程序的binder_ioctl函数,我们只关注BINDER_SET_CONTEXT_MGR命令:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. staticlongbinder_ioctl(structfile *filp, unsignedintcmd, unsignedlongarg)
  2. {
  3. intret;
  4. structbinder_proc *proc = filp->private_data;
  5. structbinder_thread *thread;
  6. unsigned intsize = _IOC_SIZE(cmd);
  7. void__user *ubuf = (void__user *)arg;
  8. /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/
  9. ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
  10. if(ret)
  11. returnret;
  12. mutex_lock(&binder_lock);
  13. thread= binder_get_thread(proc);
  14. if(thread== NULL) {
  15. ret = -ENOMEM;
  16. gotoerr;
  17. }
  18. switch(cmd) {
  19. ......
  20. caseBINDER_SET_CONTEXT_MGR:
  21. if(binder_context_mgr_node != NULL) {
  22. printk(KERN_ERR "binder: BINDER_SET_CONTEXT_MGR already set\n");
  23. ret = -EBUSY;
  24. gotoerr;
  25. }
  26. if(binder_context_mgr_uid != -1) {
  27. if(binder_context_mgr_uid != current->cred->euid) {
  28. printk(KERN_ERR "binder: BINDER_SET_"
  29. "CONTEXT_MGR bad uid %d != %d\n",
  30. current->cred->euid,
  31. binder_context_mgr_uid);
  32. ret = -EPERM;
  33. gotoerr;
  34. }
  35. else
  36. binder_context_mgr_uid = current->cred->euid;
  37. binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
  38. if(binder_context_mgr_node == NULL) {
  39. ret = -ENOMEM;
  40. gotoerr;
  41. }
  42. binder_context_mgr_node->local_weak_refs++;
  43. binder_context_mgr_node->local_strong_refs++;
  44. binder_context_mgr_node->has_strong_ref = 1;
  45. binder_context_mgr_node->has_weak_ref = 1;
  46. break;
  47. ......
  48. default:
  49. ret = -EINVAL;
  50. gotoerr;
  51. }
  52. ret = 0;
  53. err:
  54. if(thread)
  55. thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
  56. mutex_unlock(&binder_lock);
  57. wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
  58. if(ret && ret != -ERESTARTSYS)
  59. printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
  60. returnret;
  61. }

继续分析这个函数之前,又要解释两个数据结构了,一个是struct binder_thread结构体,顾名思久,它表示一个线程,这里就是执行binder_become_context_manager函数的线程了。

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. structbinder_thread {
  2. structbinder_proc *proc;
  3. structrb_node rb_node;
  4. intpid;
  5. intlooper;
  6. structbinder_transaction *transaction_stack;
  7. structlist_head todo;
  8. uint32_t return_error; /* Write failed, return error code in read buf */
  9. uint32_t return_error2; /* Write failed, return error code in read */
  10. /* buffer. Used when sending a reply to a dead process that */
  11. /* we are also waiting on */
  12. wait_queue_head_t wait;
  13. structbinder_stats stats;
  14. };

proc表示这个线程所属的进程。struct binder_proc有一个成员变量threads,它的类型是rb_root,它表示一查红黑树,把属于这个进程的所有线程都组织起来,struct binder_thread的成员变量rb_node就是用来链入这棵红黑树的节点了。looper成员变量表示线程的状态,它可以取下面这几个值:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. enum{
  2. BINDER_LOOPER_STATE_REGISTERED  = 0x01,
  3. BINDER_LOOPER_STATE_ENTERED     = 0x02,
  4. BINDER_LOOPER_STATE_EXITED      = 0x04,
  5. BINDER_LOOPER_STATE_INVALID     = 0x08,
  6. BINDER_LOOPER_STATE_WAITING     = 0x10,
  7. BINDER_LOOPER_STATE_NEED_RETURN = 0x20
  8. };

其余的成员变量,transaction_stack表示线程正在处理的事务,todo表示发往该线程的数据列表,return_error和return_error2表示操作结果返回码,wait用来阻塞线程等待某个事件的发生,stats用来保存一些统计信息。这些成员变量遇到的时候再分析它们的作用。

另外一个数据结构是struct binder_node,它表示一个binder实体:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. structbinder_node {
  2. intdebug_id;
  3. structbinder_work work;
  4. union{
  5. structrb_node rb_node;
  6. structhlist_node dead_node;
  7. };
  8. structbinder_proc *proc;
  9. structhlist_head refs;
  10. intinternal_strong_refs;
  11. intlocal_weak_refs;
  12. intlocal_strong_refs;
  13. void__user *ptr;
  14. void__user *cookie;
  15. unsigned has_strong_ref : 1;
  16. unsigned pending_strong_ref : 1;
  17. unsigned has_weak_ref : 1;
  18. unsigned pending_weak_ref : 1;
  19. unsigned has_async_transaction : 1;
  20. unsigned accept_fds : 1;
  21. intmin_priority : 8;
  22. structlist_head async_todo;
  23. };

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函数获得线程信息,我们来看一下这个函数:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. staticstructbinder_thread *binder_get_thread(structbinder_proc *proc)
  2. {
  3. structbinder_thread *thread= NULL;
  4. structrb_node *parent = NULL;
  5. structrb_node **p = &proc->threads.rb_node;
  6. while(*p) {
  7. parent = *p;
  8. thread= rb_entry(parent,structbinder_thread, rb_node);
  9. if(current->pid <thread->pid)
  10. p = &(*p)->rb_left;
  11. elseif(current->pid >thread->pid)
  12. p = &(*p)->rb_right;
  13. else
  14. break;
  15. }
  16. if(*p == NULL) {
  17. thread= kzalloc(sizeof(*thread), GFP_KERNEL);
  18. if(thread== NULL)
  19. returnNULL;
  20. binder_stats.obj_created[BINDER_STAT_THREAD]++;
  21. thread->proc = proc;
  22. thread->pid = current->pid;
  23. init_waitqueue_head(&thread->wait);
  24. INIT_LIST_HEAD(&thread->todo);
  25. rb_link_node(&thread->rb_node, parent, p);
  26. rb_insert_color(&thread->rb_node, &proc->threads);
  27. thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
  28. thread->return_error = BR_OK;
  29. thread->return_error2 = BR_OK;
  30. }
  31. returnthread;
  32. }

这里把当前线程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,它定义如下:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. staticstructbinder_node *binder_context_mgr_node;
  2. 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实体:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. staticstructbinder_node *
  2. binder_new_node(structbinder_proc *proc,void__user *ptr,void__user *cookie)
  3. {
  4. structrb_node **p = &proc->nodes.rb_node;
  5. structrb_node *parent = NULL;
  6. structbinder_node *node;
  7. while(*p) {
  8. parent = *p;
  9. node = rb_entry(parent, structbinder_node, rb_node);
  10. if(ptr < node->ptr)
  11. p = &(*p)->rb_left;
  12. elseif(ptr > node->ptr)
  13. p = &(*p)->rb_right;
  14. else
  15. returnNULL;
  16. }
  17. node = kzalloc(sizeof(*node), GFP_KERNEL);
  18. if(node == NULL)
  19. returnNULL;
  20. binder_stats.obj_created[BINDER_STAT_NODE]++;
  21. rb_link_node(&node->rb_node, parent, p);
  22. rb_insert_color(&node->rb_node, &proc->nodes);
  23. node->debug_id = ++binder_last_id;
  24. node->proc = proc;
  25. node->ptr = ptr;
  26. node->cookie = cookie;
  27. node->work.type = BINDER_WORK_NODE;
  28. INIT_LIST_HEAD(&node->work.entry);
  29. INIT_LIST_HEAD(&node->async_todo);
  30. if(binder_debug_mask & BINDER_DEBUG_INTERNAL_REFS)
  31. printk(KERN_INFO "binder: %d:%d node %d u%p c%p created\n",
  32. proc->pid, current->pid, node->debug_id,
  33. node->ptr, node->cookie);
  34. returnnode;
  35. }

注意,这里传进来的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函数返回之前,执行了下面语句:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. if(thread)
  2. 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文件中:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. voidbinder_loop(structbinder_state *bs, binder_handler func)
  2. {
  3. intres;
  4. structbinder_write_read bwr;
  5. unsigned readbuf[32];
  6. bwr.write_size = 0;
  7. bwr.write_consumed = 0;
  8. bwr.write_buffer = 0;
  9. readbuf[0] = BC_ENTER_LOOPER;
  10. binder_write(bs, readbuf, sizeof(unsigned));
  11. for(;;) {
  12. bwr.read_size = sizeof(readbuf);
  13. bwr.read_consumed = 0;
  14. bwr.read_buffer = (unsigned) readbuf;
  15. res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
  16. if(res < 0) {
  17. LOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
  18. break;
  19. }
  20. res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
  21. if(res == 0) {
  22. LOGE("binder_loop: unexpected reply?!\n");
  23. break;
  24. }
  25. if(res < 0) {
  26. LOGE("binder_loop: io error %d %s\n", res, strerror(errno));
  27. break;
  28. }
  29. }
  30. }

首先是通过binder_write函数执行BC_ENTER_LOOPER命令告诉Binder驱动程序, Service Manager要进入循环了。

这里又要介绍一下设备文件/dev/binder文件操作函数ioctl的操作码BINDER_WRITE_READ了,首先看定义:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. #define BINDER_WRITE_READ           _IOWR('b', 1, struct binder_write_read)

这个io操作码有一个参数,形式为struct binder_write_read:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. structbinder_write_read {
  2. signedlongwrite_size;/* bytes to write */
  3. signedlongwrite_consumed;/* bytes consumed by driver */
  4. unsigned longwrite_buffer;
  5. signedlongread_size;/* bytes to read */
  6. signedlongread_consumed;/* bytes consumed by driver */
  7. unsigned longread_buffer;
  8. };

这里顺便说一下,用户空间程序和Binder驱动程序交互大多数都是通过BINDER_WRITE_READ命令的,write_bufffer和read_buffer所指向的数据结构还指定了具体要执行的操作,write_bufffer和read_buffer所指向的结构体是struct binder_transaction_data:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. structbinder_transaction_data {
  2. /* The first two are only used for bcTRANSACTION and brTRANSACTION,
  3. * identifying the target and contents of the transaction.
  4. */
  5. union{
  6. size_thandle;/* target descriptor of command transaction */
  7. void*ptr;/* target descriptor of return transaction */
  8. } target;
  9. void*cookie;/* target object cookie */
  10. unsigned intcode;/* transaction command */
  11. /* General information about the transaction. */
  12. unsigned intflags;
  13. pid_t       sender_pid;
  14. uid_t       sender_euid;
  15. size_tdata_size;/* number of bytes of data */
  16. size_toffsets_size;/* number of bytes of offsets */
  17. /* If this transaction is inline, the data immediately
  18. * follows here; otherwise, it ends with a pointer to
  19. * the data buffer.
  20. */
  21. union{
  22. struct{
  23. /* transaction data */
  24. constvoid*buffer;
  25. /* offsets from buffer to flat_binder_object structs */
  26. constvoid*offsets;
  27. } ptr;
  28. uint8_t buf[8];
  29. } data;
  30. };

有一个联合体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成员变量表示事务标志:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. enumtransaction_flags {
  2. TF_ONE_WAY  = 0x01, /* this is a one-way call: async, no return */
  3. TF_ROOT_OBJECT  = 0x04, /* contents are the component's root object */
  4. TF_STATUS_CODE  = 0x08, /* contents are a 32-bit status code */
  5. TF_ACCEPT_FDS   = 0x10, /* allow replies with file descriptors */
  6. };

每一个标志位所表示的意义看注释就行了,遇到时再具体分析。

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 来表示:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. /*
  2. * This is the flattened representation of a Binder object for transfer
  3. * between processes.  The 'offsets' supplied as part of a binder transaction
  4. * contains offsets into the data where these structures occur.  The Binder
  5. * driver takes care of re-writing the structure type and data as it moves
  6. * between processes.
  7. */
  8. structflat_binder_object {
  9. /* 8 bytes for large_flat_header. */
  10. unsigned longtype;
  11. unsigned longflags;
  12. /* 8 bytes of data. */
  13. union{
  14. void*binder;/* local object */
  15. signedlonghandle;/* remote object */
  16. };
  17. /* extra data associated with local object */
  18. void*cookie;
  19. };

type表示Binder对象的类型,它取值如下所示:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. enum{
  2. BINDER_TYPE_BINDER  = B_PACK_CHARS('s','b','*', B_TYPE_LARGE),
  3. BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w','b','*', B_TYPE_LARGE),
  4. BINDER_TYPE_HANDLE  = B_PACK_CHARS('s','h','*', B_TYPE_LARGE),
  5. BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w','h','*', B_TYPE_LARGE),
  6. BINDER_TYPE_FD      = B_PACK_CHARS('f','d','*', B_TYPE_LARGE),
  7. };

flags表示Binder对象的标志,该域只对第一次传递Binder实体时有效,因为此刻驱动需要在内核中创建相应的实体节点,有些参数需要从该域取出。

type和flags的具体意义可以参考Android Binder设计与实现一文。

最后,binder表示这是一个Binder实体,handle表示这是一个Binder引用,当这是一个Binder实体时,cookie才有意义,表示附加数据,由进程自己解释。

数据结构分析完了,回到binder_loop函数中,首先是执行BC_ENTER_LOOPER命令:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. readbuf[0] = BC_ENTER_LOOPER;
  2. binder_write(bs, readbuf, sizeof(unsigned));

进入到binder_write函数中:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. intbinder_write(structbinder_state *bs,void*data, unsigned len)
  2. {
  3. structbinder_write_read bwr;
  4. intres;
  5. bwr.write_size = len;
  6. bwr.write_consumed = 0;
  7. bwr.write_buffer = (unsigned) data;
  8. bwr.read_size = 0;
  9. bwr.read_consumed = 0;
  10. bwr.read_buffer = 0;
  11. res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
  12. if(res < 0) {
  13. fprintf(stderr,"binder_write: ioctl failed (%s)\n",
  14. strerror(errno));
  15. }
  16. returnres;
  17. }

注意这里的binder_write_read变量bwr,write_size大小为4,表示write_buffer缓冲区大小为4,它的内容是一个BC_ENTER_LOOPER命令协议号,read_buffer为空。接着又是调用ioctl函数进入到Binder驱动程序的binder_ioctl函数,这里我们也只是关注BC_ENTER_LOOPER相关的逻辑:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. staticlongbinder_ioctl(structfile *filp, unsignedintcmd, unsignedlongarg)
  2. {
  3. intret;
  4. structbinder_proc *proc = filp->private_data;
  5. structbinder_thread *thread;
  6. unsigned intsize = _IOC_SIZE(cmd);
  7. void__user *ubuf = (void__user *)arg;
  8. /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/
  9. ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
  10. if(ret)
  11. returnret;
  12. mutex_lock(&binder_lock);
  13. thread= binder_get_thread(proc);
  14. if(thread== NULL) {
  15. ret = -ENOMEM;
  16. gotoerr;
  17. }
  18. switch(cmd) {
  19. caseBINDER_WRITE_READ: {
  20. structbinder_write_read bwr;
  21. if(size !=sizeof(structbinder_write_read)) {
  22. ret = -EINVAL;
  23. gotoerr;
  24. }
  25. if(copy_from_user(&bwr, ubuf,sizeof(bwr))) {
  26. ret = -EFAULT;
  27. gotoerr;
  28. }
  29. if(binder_debug_mask & BINDER_DEBUG_READ_WRITE)
  30. printk(KERN_INFO "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",
  31. proc->pid, thread->pid, bwr.write_size, bwr.write_buffer, bwr.read_size, bwr.read_buffer);
  32. if(bwr.write_size > 0) {
  33. ret = binder_thread_write(proc, thread, (void__user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
  34. if(ret < 0) {
  35. bwr.read_consumed = 0;
  36. if(copy_to_user(ubuf, &bwr,sizeof(bwr)))
  37. ret = -EFAULT;
  38. gotoerr;
  39. }
  40. }
  41. if(bwr.read_size > 0) {
  42. ret = binder_thread_read(proc, thread, (void__user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
  43. if(!list_empty(&proc->todo))
  44. wake_up_interruptible(&proc->wait);
  45. if(ret < 0) {
  46. if(copy_to_user(ubuf, &bwr,sizeof(bwr)))
  47. ret = -EFAULT;
  48. gotoerr;
  49. }
  50. }
  51. if(binder_debug_mask & BINDER_DEBUG_READ_WRITE)
  52. printk(KERN_INFO "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",
  53. proc->pid, thread->pid, bwr.write_consumed, bwr.write_size, bwr.read_consumed, bwr.read_size);
  54. if(copy_to_user(ubuf, &bwr,sizeof(bwr))) {
  55. ret = -EFAULT;
  56. gotoerr;
  57. }
  58. break;
  59. }
  60. ......
  61. default:
  62. ret = -EINVAL;
  63. gotoerr;
  64. }
  65. ret = 0;
  66. err:
  67. if(thread)
  68. thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
  69. mutex_unlock(&binder_lock);
  70. wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
  71. if(ret && ret != -ERESTARTSYS)
  72. printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
  73. returnret;
  74. }

函数前面的代码就不解释了,同前面调用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相关的代码:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. int
  2. binder_thread_write(structbinder_proc *proc,structbinder_thread *thread,
  3. void__user *buffer,intsize,signedlong*consumed)
  4. {
  5. uint32_t cmd;
  6. void__user *ptr = buffer + *consumed;
  7. void__user *end = buffer + size;
  8. while(ptr < end &&thread->return_error == BR_OK) {
  9. if(get_user(cmd, (uint32_t __user *)ptr))
  10. return-EFAULT;
  11. ptr += sizeof(uint32_t);
  12. if(_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
  13. binder_stats.bc[_IOC_NR(cmd)]++;
  14. proc->stats.bc[_IOC_NR(cmd)]++;
  15. thread->stats.bc[_IOC_NR(cmd)]++;
  16. }
  17. switch(cmd) {
  18. ......
  19. caseBC_ENTER_LOOPER:
  20. if(binder_debug_mask & BINDER_DEBUG_THREADS)
  21. printk(KERN_INFO "binder: %d:%d BC_ENTER_LOOPER\n",
  22. proc->pid, thread->pid);
  23. if(thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
  24. thread->looper |= BINDER_LOOPER_STATE_INVALID;
  25. binder_user_error("binder: %d:%d ERROR:"
  26. " BC_ENTER_LOOPER called after "
  27. "BC_REGISTER_LOOPER\n",
  28. proc->pid, thread->pid);
  29. }
  30. thread->looper |= BINDER_LOOPER_STATE_ENTERED;
  31. break;
  32. ......
  33. default:
  34. printk(KERN_ERR "binder: %d:%d unknown command %d\n", proc->pid,thread->pid, cmd);
  35. return-EINVAL;
  36. }
  37. *consumed = ptr - buffer;
  38. }
  39. return0;
  40. }

回忆前面执行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循环:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. for(;;) {
  2. bwr.read_size = sizeof(readbuf);
  3. bwr.read_consumed = 0;
  4. bwr.read_buffer = (unsigned) readbuf;
  5. res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
  6. if(res < 0) {
  7. LOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
  8. break;
  9. }
  10. res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
  11. if(res == 0) {
  12. LOGE("binder_loop: unexpected reply?!\n");
  13. break;
  14. }
  15. if(res < 0) {
  16. LOGE("binder_loop: io error %d %s\n", res, strerror(errno));
  17. break;
  18. }
  19. }

又是执行一个ioctl命令,注意,这里的bwr参数各个成员的值:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. bwr.write_size = 0;
  2. bwr.write_consumed = 0;
  3. bwr.write_buffer = 0;
  4. readbuf[0] = BC_ENTER_LOOPER;
  5. bwr.read_size = sizeof(readbuf);
  6. bwr.read_consumed = 0;
  7. bwr.read_buffer = (unsigned) readbuf;

再次进入到binder_ioctl函数:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. staticlongbinder_ioctl(structfile *filp, unsignedintcmd, unsignedlongarg)
  2. {
  3. intret;
  4. structbinder_proc *proc = filp->private_data;
  5. structbinder_thread *thread;
  6. unsigned intsize = _IOC_SIZE(cmd);
  7. void__user *ubuf = (void__user *)arg;
  8. /*printk(KERN_INFO "binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/
  9. ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
  10. if(ret)
  11. returnret;
  12. mutex_lock(&binder_lock);
  13. thread= binder_get_thread(proc);
  14. if(thread== NULL) {
  15. ret = -ENOMEM;
  16. gotoerr;
  17. }
  18. switch(cmd) {
  19. caseBINDER_WRITE_READ: {
  20. structbinder_write_read bwr;
  21. if(size !=sizeof(structbinder_write_read)) {
  22. ret = -EINVAL;
  23. gotoerr;
  24. }
  25. if(copy_from_user(&bwr, ubuf,sizeof(bwr))) {
  26. ret = -EFAULT;
  27. gotoerr;
  28. }
  29. if(binder_debug_mask & BINDER_DEBUG_READ_WRITE)
  30. printk(KERN_INFO "binder: %d:%d write %ld at %08lx, read %ld at %08lx\n",
  31. proc->pid, thread->pid, bwr.write_size, bwr.write_buffer, bwr.read_size, bwr.read_buffer);
  32. if(bwr.write_size > 0) {
  33. ret = binder_thread_write(proc, thread, (void__user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
  34. if(ret < 0) {
  35. bwr.read_consumed = 0;
  36. if(copy_to_user(ubuf, &bwr,sizeof(bwr)))
  37. ret = -EFAULT;
  38. gotoerr;
  39. }
  40. }
  41. if(bwr.read_size > 0) {
  42. ret = binder_thread_read(proc, thread, (void__user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
  43. if(!list_empty(&proc->todo))
  44. wake_up_interruptible(&proc->wait);
  45. if(ret < 0) {
  46. if(copy_to_user(ubuf, &bwr,sizeof(bwr)))
  47. ret = -EFAULT;
  48. gotoerr;
  49. }
  50. }
  51. if(binder_debug_mask & BINDER_DEBUG_READ_WRITE)
  52. printk(KERN_INFO "binder: %d:%d wrote %ld of %ld, read return %ld of %ld\n",
  53. proc->pid, thread->pid, bwr.write_consumed, bwr.write_size, bwr.read_consumed, bwr.read_size);
  54. if(copy_to_user(ubuf, &bwr,sizeof(bwr))) {
  55. ret = -EFAULT;
  56. gotoerr;
  57. }
  58. break;
  59. }
  60. ......
  61. default:
  62. ret = -EINVAL;
  63. gotoerr;
  64. }
  65. ret = 0;
  66. err:
  67. if(thread)
  68. thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
  69. mutex_unlock(&binder_lock);
  70. wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
  71. if(ret && ret != -ERESTARTSYS)
  72. printk(KERN_INFO "binder: %d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
  73. returnret;
  74. }

这次,bwr.write_size等于0,于是不会执行binder_thread_write函数,bwr.read_size等于32,于是进入到binder_thread_read函数:

[cpp] view plaincopy

在CODE上查看代码片派生到我的代码片

  1. staticint
  2. binder_thread_read(structbinder_proc *proc,structbinder_thread *thread,
  3. void__user *buffer,intsize,signedlong*consumed,intnon_block)
  4. {
  5. void__user *ptr = buffer + *consumed;
  6. void__user *end = buffer + size;
  7. intret = 0;
  8. intwait_for_proc_work;
  9. if(*consumed == 0) {
  10. if(put_user(BR_NOOP, (uint32_t __user *)ptr))
  11. return-EFAULT;
  12. ptr += sizeof(uint32_t);
  13. }
  14. retry:
  15. wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);
  16. if(thread->return_error != BR_OK && ptr < end) {
  17. if(thread->return_error2 != BR_OK) {
  18. if(put_user(thread->return_error2, (uint32_t __user *)ptr))
  19. return-EFAULT;
  20. ptr += sizeof(uint32_t);
  21. if(ptr == end)
  22. gotodone;
  23. thread->return_error2 = BR_OK;
  24. }
  25. if(put_user(thread->return_error, (uint32_t __user *)ptr))
  26. return-EFAULT;
  27. ptr += sizeof(uint32_t);
  28. thread->return_error = BR_OK;
  29. gotodone;
  30. }
  31. thread->looper |= BINDER_LOOPER_STATE_WAITING;
  32. if(wait_for_proc_work)
  33. proc->ready_threads++;
  34. mutex_unlock(&binder_lock);
  35. if(wait_for_proc_work) {
  36. if(!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
  37. BINDER_LOOPER_STATE_ENTERED))) {
  38. binder_user_error("binder: %d:%d ERROR: Thread waiting "
  39. "for process work before calling BC_REGISTER_"
  40. "LOOPER or BC_ENTER_LOOPER (state %x)\n",
  41. proc->pid, thread->pid,thread->looper);
  42. wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
  43. }
  44. binder_set_nice(proc->default_priority);
  45. if(non_block) {
  46. if(!binder_has_proc_work(proc,thread))
  47. ret = -EAGAIN;
  48. else
  49. ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));
  50. else{
  51. if(non_block) {
  52. if(!binder_has_thread_work(thread))
  53. ret = -EAGAIN;
  54. else
  55. ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));
  56. }
  57. .......
  58. }

传入的参数*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

我的同类文章

  • 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守护进程之路
id="iframeu1607657_0" src="http://pos.baidu.com/dcim?rdid=1607657&dc=2&di=u1607657&dri=0&dis=0&dai=1&ps=35010x339&dcb=BAIDU_SSP_define&dtm=HTML_POST&dvi=0.0&dci=-1&dpt=none&tsr=0&tpr=1474438155992&ti=%E6%B5%85%E8%B0%88Service%20Manager%E6%88%90%E4%B8%BAAndroid%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E4%BF%A1%EF%BC%88IPC%EF%BC%89%E6%9C%BA%E5%88%B6Binder%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B%E4%B9%8B%E8%B7%AF%20-%20%E8%80%81%E7%BD%97%E7%9A%84Andr&ari=2&dbv=2&drs=1&pcs=1216x615&pss=1216x35028&cfv=0&cpl=5&chi=1&cce=true&cec=UTF-8&tlm=1474438155&rw=615&ltu=http%3A%2F%2Fblog.csdn.net%2Fluoshengyang%2Farticle%2Fdetails%2F6621566&ltr=http%3A%2F%2Fblog.csdn.net%2Fluoshengyang%2Farticle%2Fdetails%2F6677029&ecd=1&psr=1600x900&par=1535x876&pis=-1x-1&ccd=24&cja=false&cmi=7&col=en-US&cdo=-1&tcn=1474438156&qn=f76f0403340d399b&tt=1474438155786.219.224.232" width="728" height="90" align="center,center" vspace="0" hspace="0" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" allowtransparency="true" style="border-width: 0px; border-style: initial; vertical-align: bottom; margin: 0px;">
查看评论
43楼 BigGod_ming 2016-09-02 22:50发表 [回复]
没接触过C 看起来难度有点大。。有什么好的建议吗
42楼 baidu_20043811 2016-09-01 10:45发表 [回复]
进入android行业两年了,感觉需要学的很多,向老罗看齐。
41楼 寄意兰州 2016-05-23 18:18发表 [回复]
binder_become_context_manager来通知Binder驱动程序自己是Binder机制的上下文管理者,这个就能成为守护进程吗?

守护进程不是要六七步才能成为吗,为什么没有相关成为守护进程的代码?

40楼 xx天下大同 2016-01-15 19:01发表 [回复]
感觉格式对新人来说很不明了,遂做了修改,http://blog.csdn.net/xx326664162/article/details/50519294
39楼 liao_hb 2016-01-07 20:09发表 [回复]
罗老大,问一个问题,同一个物理页面,一方面映射到服务进程虚拟地址空间,一方面映射到内核虚拟地址空间,一方面映射到客户端进程虚拟地址空间,这样没有copy,可以通信吗?如果可以,google为什么不这样干?有啥弊端吗
38楼 michaelzhang113 2015-02-02 15:18发表 [回复]
罗工你好,我想请问binder调用机制有什么比较好的技巧和方法来调试,比较快速的定位和找到问题
37楼 sYao100 2015-01-05 16:19发表 [回复]
罗老师,你好~
请教一个问题:binder的权限是proc是否拥有对binder IPC操作权限(Binder IPC:"impersonate", "call", "set_context_mgr", "transfer"),如何禁止某一个client访问某一个server,具体策略是如何定制的?
36楼 sYao100 2015-01-05 16:18发表 [回复]
罗老师,你好~
请教一个问题:binder的权限是proc是否拥有对binder IPC操作权限(Binder IPC:"impersonate", "call", "set_context_mgr", "transfer"),如何禁止某一个client访问某一个server,具体策略是如何定制的?
35楼 低调小一 2014-10-22 19:53发表 [回复]
确实强大,照着源码还没读懂。。
34楼 XHunterX 2014-08-18 11:23发表 [回复]
请教一下,既然binder设备只用到那么几M的内存((3G + 896M + 8M) ~ 4G),那怎么够系统中成百上千的各类service使用呢?
33楼 andevele 2014-08-05 16:08发表 [回复]
老罗,一个问题,你在描述binder_node函数时,有一段代码:
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。

32楼 happy08god 2014-02-12 18:21发表 [回复]
我也写了篇讲述service manager的文章——
http://blog.csdn.net/happy08god/article/details/18730461

当我阅读了LZ的文章后,觉得自己跟您的差距实在太大了。看来学习的路还很长啊!!

31楼 yan06035 2013-05-11 22:45发表 [回复]
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组织数据?
还是说是在某些情况下使用?

Re: 罗升阳 2013-05-13 12:11发表 [回复]
回复yan06035:write_buffer和read_buffer描述的都是一段raw buffer,它的具体格式要看要执行的功能是什么,例如,BC_TRANSACTION和BR_TRANSACTION功能使用的buffer就是用binder_transaction_data结构体来描述。
30楼 rickystudio 2013-05-07 08:22发表 [回复]
罗老师,flat_binder_object和binder_node都是binder实体,有什么区别?

Re: 罗升阳 2013-05-07 09:24发表 [回复]
回复rickystudio:前者是用来在用户空间和内核空间传递Binder对象的,后者是在内核空间用来描述Binder对象的状态的。
29楼 rickystudio 2013-05-06 13:50发表 [回复]
罗老师,
thread->pid = current->pid
current->pid是进程的pid,这里怎么赋值给了线程pid?
28楼 rickystudio 2013-05-02 15:16发表 [回复]
昨天买了本罗老师的书,书上还是和博客的不一样,感觉书上的更好。超喜欢。多谢好书!
27楼 rickystudio 2013-05-02 01:06发表 [回复]
罗老师,binder_context_mgr_node,这个东西哪里有用到?为什么要创建一个实体?是不是每个service都会有一个binder_context_mgr_node实体。驱动来管理这些实体?

Re: 罗升阳 2013-05-02 09:37发表 [回复]
回复rickystudio:整个系统只能有一个binder_context_mgr_node,用来标识Service Manager。
26楼 BadPattern 2013-02-27 23:09发表 [回复]
有个小白的问题要问,mmap()系统调用我一开始的理解是将指定fd的文件映射到调用进程的虚拟地址空间,从而调用进程可以通过获取到的虚拟地址来访问该文件。
那是不是mmap()系统调用的行为由各自驱动程序定义的,也可以在binder_mmap()函数中做其他事情,如这里就分配了物理页面并映射到内核空间和用户空间,从而调用进程操作的实际上是分配的物理页面啊?

Re: BadPattern 2013-02-27 23:13发表 [回复]
回复dr8737010:因为这里的/dev/binder文件不像普通的文件,普通的文件使用mmap映射以后,可以利用得到的指针来读取文件的内容,binder是字符设备,有点不理解 ~_~

Re: 罗升阳 2013-02-28 09:55发表 [回复]
回复dr8737010:在Linux系统中,有一点是理解的:什么东西都可以看作是文件,然后通过文件系统接口来访问,所以当你拿到一个文件描述符的时候,并不是说你访问的是磁盘上的一个文件。基于这一点,就可以从广义上来理解mmap,它只是要求将一段虚拟地址映射到物理内存来,至于这块物理内存的内容是怎么来的,它就不管了。这块物理内存的内容可以来自磁盘上的文件,也可以是内核在运行时生成的,这完全是由驱动模块自己来决定的,也就是说由它所提供的mmap映射函数来决定的。
如果你研究一下Binder驱动的代码,就会发现虽然/dev/binder虽然是一个字符类型的设备文件,但是它不提供有读写操作,即不能对它进行read和write操作,而且用户空间程序对它进行内存映射的时候,只提供了读访问权限。也就是说,对/dev/binder这个设备文件,你只能先将它映射到内存,然后再读取它的内容,但是不能写它的内容。

Re: BadPattern 2013-02-28 12:02发表 [回复]
回复Luoshengyang:多谢回复!
那是不是可以这样理解这种映射:
SM通过binder驱动申请了一块128*1024大小的物理内存,内核地址空间也会映射上这块内存,SM的client端要想与SM通信,就指明binder驱动我需要0号引用的东西,驱动会操作内核中映射的地址空间,从而就影响到SM中的物理内存?
对这里面的关系还是云里雾里的。

Re: 罗升阳 2013-02-28 13:14发表 [回复]
回复dr8737010:数据传输的流程是这样的:
1. Client将数据从用户空间传输到Binder驱动;
2. Binder驱动将第1步得到的数据拷贝到Service通过mmap申请得到的那块物理空间;
3. Binder驱动将第2步得到的物理空间对应的虚拟地址传递给Service的用户空间;
4. Service的用户空间通过Binder驱动传递过来的虚拟地址来访问Client传输过来的数据。
整个过程只有第2步是需要拷贝数据的,这也是Binder进程间通信机制的精华所在。
25楼 zhaoyu_android4311 2013-02-19 08:53发表 [回复]
老罗问个binder问题,最近开源码,发现有一对函数总是出现,看源码注释理解不是很明白, Binder.clearCallingIdentity(),Binder.restoreCallingIdentity(),这对方法之间总是夹着服务端的一些本地函数(即本地化于服务端,未对客户端开放,只能服务端自己调用),不知道什么意思这对函数,似乎和进程权限有关看注释说,望老罗百忙之中解答一下

Re: 罗升阳 2013-02-19 15:22发表 [回复]
回复zhaoyu_android4311:Client和Server执行Binder IPC时,Binder驱动会将Client的PID和UID传递给Server,以便Server可以用来进行权限验证。
在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。
24楼 DOYOUENJOYIT 2013-01-21 11:21发表 [回复]
老罗,我想问一下:是不是这样,一个进程中有很多Bn对象(也就是IBinder对象),IPCTthreadState从/dev/binder接收到数据之后,交给相应的Bn对象去处理? 如果是这样,那么每次新建一个Bn对象的时候是如何注册到系统中去的呢?
23楼 simonforever 2013-01-18 09:58发表 [回复]
问一个初级的问题,frameworks/base/cmds/servicemanager/binder.c 文件里的binder_open函数中的 bs->fd = open("/dev/binder", O_RDWR);怎么调到Binder驱动程序的binder_open函数???

Re: 罗升阳 2013-01-18 10:13发表 [回复]
回复shelina:这个说来话长,你得去看看Linux的文件系统以及驱动模块是如何实现的,《Linux内核源代码情景分析》这本书有说。
22楼 edmond999 2013-01-07 18:39发表 [回复]
讲解很好,书也买了,但是有些疑问,在非阻塞模式下,binder_thread_read函数会返回-EGAIN,这种情况下,ioctl函数返回值是负数,是不是就要推出 binder_loop的for循环了,这样子不是程序要退出了,是这样的嘛,求指教,谢谢。

Re: 罗升阳 2013-01-07 19:10发表 [回复]
回复edmond999:Service Manager是以阻塞方式打开设备文件/dev/binder的,所以不会出现你说的情况~
21楼 woluzhi 2013-01-07 17:32发表 [回复]
请教个比较菜的问题
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呢?

Re: 罗升阳 2013-01-07 17:43发表 [回复]
回复woluzhi:没什么区别,纯属个人爱好。如果换成struct rb_node *p,后面调用rb_link_node时,最后一个参数就要写成&p。

Re: woluzhi 2013-01-07 22:25发表 [回复]
回复Luoshengyang:罗老师, 真敬业啊 这么快就回来,受教了,非常感谢。
20楼 aaronlibra 2012-12-20 16:41发表 [回复]
罗老师,你好!
请问一下,我在log中发现这样的信息:
binder: 1194: binder_alloc_buf, no vma
这一般是什么原因造成的?

Re: taozuiqizhong 2015-11-13 16:17发表 [回复]
回复aaronlibra:传说,这log一般都是请求时service已经挂掉了
Re: 罗升阳 2012-12-20 16:47发表 [回复]
回复aaronlibra:没有将/dev/binder设备文件map到进程,或者map之后,又会unmap了。

Re: aaronlibra 2012-12-20 17:03发表 [回复]
回复Luoshengyang:谢谢罗老师这么快就回复了!
详细的一段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可能是什么原因导致的?

Re: aaronlibra 2012-12-20 17:07发表 [回复]
回复aaronlibra:另一个问题也比较类似:
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是一个在线视频播放器,出现的问题现象是视频播放卡住,然后重启。

Re: 罗升阳 2012-12-20 18:45发表 [回复]
回复aaronlibra:进程退出也会导致vma为空,估计就是因为进程退出了。
19楼 pfgmylove 2012-12-05 16:20发表 [回复]
你好,文章中的binder和Binder有什么联系吗,在android源码中有binder.c和Binder.cpp这两个文件又有什么联系呢?

Re: 罗升阳 2012-12-05 16:29发表 [回复]
回复pfgmylove:binder.c是用来实现binder驱动的,binder.cpp是用来实现binder库,而binder库是用来封装对binder驱动的操作的。
18楼 Mr_print 2012-11-26 22:32发表 [回复]
老罗您好!这几天看了您的binder的几篇文章,受益匪浅,思路很清晰,很好!我是将android源码导入source insight看的,但是有个问题,为什么我一直找不到binder_ioctl()这个函数呢,我怎么连接查找都找不到,弱弱的问一下:这个函数在哪个文件里啊...?谢谢了

Re: 罗升阳 2012-11-27 00:24发表 [回复]
回复key1213:在内核代码里面:drivers/staging/android/binder.c

Re: Mr_print 2012-11-27 09:11发表 [回复]
回复Luoshengyang:好的,谢谢啦
17楼 3kqing 2012-10-24 15:35发表 [回复]
能不能提供一种解决方案或思路?
16楼 3kqing 2012-10-24 15:35发表 [回复]
您好,想请教一个问题:Activity和Service之间的通信问题,Service要在后台一直运行,有时候需求启动APP然后要实现APP的Activity和Service进行通信,例如Activity传一个参数给Service,然后Service返回数据,当然最开始启动Service的时候不能用绑定的方式,以为如果是绑定启动Service的话如果退出APP,Activity注销,那么Service也会注销,请问这个问题怎么解决?

Re: 罗升阳 2012-10-24 19:05发表 [回复]
回复CrossJL:你可以使用startService将Service启动在一个独立的进程中,每次Activity启动的时候,就到ServiceManager去检查这个Service是否启动了,如果启动了,就直接用,否则的话,就先将它启动起来。这样是否可以满足你的需求?
15楼 huangzhenyu1983 2012-08-20 11:18发表 [回复]
你好,有一处疑问请教:serveice_manange这个程序中的binder_open函数有一处语句:
bs->fd = open("/dev/binder", O_RDWR);这个语句是属于用户态下执行的,它调用open这个函数,也即binder_open驱动函数,是属于内核态的代码。编译serveice_manange这个独立程序的时候肯定不会把binder_open这个驱动函数编译进去的。请教,serveice_manange是如何调用到binder_open这个驱动函数de?

Re: huangzhenyu1983 2012-08-20 11:34发表 [回复]
回复huangzhenyu1983:还是说open这个函数是个系统调用?是所有文件操作的通用入口,之后内核的代码会根据一些信息来调用到binder_open这个内核函数?

Re: 罗升阳 2012-08-20 11:39发表 [回复]
回复huangzhenyu1983:是的,如果有兴趣的话,你可以看一下《Linux内核源代码情景分析》这本书,里面很详细地讲了open函数的执行过程,非常复杂~不过对于设备文件来说,内核是依靠设备号来找到对应的open函数来执行的。
Re: 罗升阳 2012-08-20 11:24发表 [回复]
回复huangzhenyu1983:1. binder_open是由binder驱动模块实现的,而binder驱动模块是内建在内核里面的,因此,内核映像本身包含有binder_open函数。
2. open函数是一个系统调用,在执行系统调用的时候,CPU会从用户态切换为内核态,等到内核态的binder_open执行完成之后,再返回到用户态。
这个知识点需要了解一下操作系统方面的知识^_^

Re: huangzhenyu1983 2012-08-20 11:37发表 [回复]
回复Luoshengyang:恩。明白了,谢谢。一开始没有想到open这个函数是个系统调用,以为open这个系统调用只是用来操作普通意义上的文件的。

Re: 罗升阳 2012-08-20 11:42发表 [回复]
回复huangzhenyu1983:这就是Linux内核强大的地方,几乎所有的东西都可以抽象成一个文件,然后通过open/ioctl/mmap来操作。。。。。。
14楼 duyouxuanduyouxuan 2012-08-16 09:24发表 [回复]
Mark
13楼 njujames 2012-08-09 20:42发表 [回复]
你好,有两个问题想请假下:
一、BnServiceManager在OnTransact函数中调用addService函数,但是在service_manager文件中是do_add_service函数,它们怎么对应起来的啊?
二、BpMediaPlayerService在调用transact时,后来怎么转成了BnMediaPlayerService调用OnTransact函数了,是不是在驱动中维持了引用与本地对象的映射关系,对于驱动来说没有进程一说,都是一样的?remote()函数返回的是BpBinder对象,然后它调用transact函数,到内核中怎么变成BBinder调用transact再调OnTransact的啊?对应的代码我没找到,可能没看明白。

Re: 罗升阳 2012-08-09 23:08发表 [回复]
回复njujames:第一个问题是这样的:Service Manager比较特殊,它的实现不是从BnServiceManager继承下来的,真正的实现是在service_manager文件中,因此,BnServiceManager的addService和service_manager文件中的do_add_service是没有对应关系的。
第二个问题是这样的:BnMediaPlayerService是一个Binder本地对象,它在内核中有一个对应的Binder实体对象,当另外一个进程要使用MediaPlayerService的时候,Binder驱动程序就会在内核创建一个Binder引用对象,同时在那个进程创建一个Binder代理对象,即一个BpMediaPlayerService对象。这四个对象是有一一对应关系的,因此,当一个进程调用BpMediaPlayerService对象的transact函数时,通过Binder驱动程序就可以找到对应的BnMediaPlayerService对象,并且调用它的OnTransact函数。这一点在我即将要出版的书中将会有非常详细的描述。

Re: njujames 2012-08-09 23:32发表 [回复]
回复Luoshengyang:忘记问下了,BC_XXX和BR_XXX, 我看的好绕啊。不太懂,我跟踪者addService这个操作到内核驱动中,就好像混乱了,参数都不知道跑哪去了。

Re: 罗升阳 2012-08-09 23:41发表 [回复]
回复njujames:同样是看一下这篇文章:http://blog.csdn.net/luoshengyang/article/details/6629298。看懂了这篇文章,基本上就可以解决你的问题了。
Re: njujames 2012-08-09 23:29发表 [回复]
嗯,谢谢。
对于第一个问题,我很想知道addService怎么调用到do_add_service的,驱动中有实现吗?
第二个问题我也知道是一一对应的,是不是在红黑树中保存了对应的关系呢?内核中的binder引用和内核中的binder实体在驱动中怎么做到的啊,这段驱动看不太懂,不知是否有详细介绍,在你的书中?回复Luoshengyang:

Re: 罗升阳 2012-08-09 23:40发表 [回复]
回复njujames:第一个问题,你可以看一下这篇文章:http://blog.csdn.net/luoshengyang/article/details/6629298。
第二个问题在我的书里面都会有讲述。为了讲清楚Binder机制,书里面花了有200页的篇幅。你可以看一下这篇文章:http://blog.csdn.net/luoshengyang/article/details/7409491,这里有详细的目录。
12楼 天才2012 2012-07-30 20:40发表 [回复]
很好奇,你在讲mmap驱动时,为何会说将内核虚拟地址和进程虚拟地址全部映射到物理页上。一般linux下面的驱动mmap就做用户空间地址到实际物理地址的映射就可以了啊,不知道为何。

Re: 罗升阳 2012-07-30 20:45发表 [回复]
回复gzzaigcn:这就是Binder进程间通信机制的特点了,进程的地址空间划分为内核空间和用户空间两部分,将它们同时映射到同一个物理页面上去,就可以减少一次内核空间和用户空间的数据拷贝。

Re: 天才2012 2012-07-30 22:34发表 [回复]
回复Luoshengyang:恩,也许这就所他的特点吧,总感觉和普通驱动mmap比复杂了很多。但是基本的mmap在用户空间执行时,也是只进行一次拷贝的啊。返回这个用户空间的地址,往地址写数值就好了。也许我还没有领悟Binder的核心所在吧。学习了
11楼 playboyNo 2012-07-26 15:27发表 [回复]
proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
10楼 playboyNo 2012-07-26 15:23发表 [回复]
"user_buffer_offset成员变量是一个ptrdiff_t类型的变量,它表示的是内核使用的虚拟地址与进程使用的虚拟地址之间的差值,即如果某个物理页面在内核空间中对应的虚拟地址是addr的话,那么这个物理页面在进程空间对应的虚拟地址就为addr + user_buffer_offset。"
-----------------
addr + user_buffer_offset 这个是否写错了?

Re: 罗升阳 2012-07-26 20:14发表 [回复]
回复playboyNo:没有错吧。看下面这个表达式:
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是一个内核空间址。
9楼 electricity 2012-04-25 15:12发表 [回复]
是不是申请空间映射的时候,必须要保证是PAGE_SIZE的整数倍。
8楼 electricity 2012-04-25 15:01发表 [回复]
你好,问个问题,在驱动binder.c 中binder_mmap函数里
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的字节不是没有被分配吗?
7楼 themass 2011-12-27 19:24发表 [回复]
楼主,我太爱你这个博客了,我学习就以这个来学习了,杨丰盛 那狗屁的书真是浮云,如果博主出书我肯定买全套的。
博主,驱动 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 等等太多的结构体了,我实在里不清楚关系了,希望博主帮忙啊。
还希望博主多分享些学习的经验啊

Re: 罗升阳 2011-12-29 13:35发表 [回复]
回复liguoqing19861028:1. proc->pages是一个物理页面指针数组,这里分配的只是指针,没有分配物理页面;
2. 不是,会根据需要来分配更多的物理页面,只是一开始的时候只分配一个;
3. 参见2的答案;
4和5一言难尽~合适的时候我会把Binder驱动程序用到的数据结构都详细介绍一遍。
Re: themass 2011-12-27 19:30发表 [回复]
回复liguoqing19861028:vm_insert_page(vma, user_page_addr, page[0]);
这个函数没搞明白,vma要映射的空间很大,为什么值传递了 一个page?
因为struct page 结构体是用来描述一个物理页的,所以上面的作用是不是把page 结构体描述的那个 物理页面 和 vma描述的 虚拟空间 对应起来?

Re: 罗升阳 2011-12-29 13:37发表 [回复]
回复liguoqing19861028:vma描述的是一段连续的虚拟地址空间,它有多少个页框,就要对应多少个物理页面,这里只不过是分它分配了一个物理页面,后面需要的时候还会继续分配。

Re: li529696 2011-12-30 10:38发表 [回复]
回复Luoshengyang:博主,mmap 函数调用在内核会分配一个虚拟内存区域,
这个区域是没有物理页面的,所以驱动有两种方法来分配页框,一种是全部分配,一种是一页一页的分,但是这种分法需要提供缺页异常函数啊。但是这里的mmap并没看到缺页异常的函数在哪?
以后用到时会分配?怎么分,调用什么函数分?这个mmap返回的地址就没见在哪里使用过。博主能不能说一下在哪里用到了这个映射?

Re: 最后一个菜鸟 2013-08-22 16:03发表 [回复]
回复li529696:vma->vm_ops = &binder_vm_ops;
Re: 罗升阳 2011-12-31 10:44发表 [回复]
回复li529696:一页一页地分配。这段虚拟地址空间的使用方式是先由binder驱动程序为其分配物理页面,然后再通知应用程序去使用,因此,不会出现缺页这种情况。当然,也要求应用程序不要主动去访问这个虚拟地址空间,否则就有问题了。物理页面分配的函数是binder_alloc_buf.
6楼 yunzhongyuekelly 2011-12-15 14:55发表 [回复]
lz,请教一个问题,之前你说的“而采用我们上面提到的方法,只需要把Client进程空间的数据拷贝一次到内核空间,然后Server与内核共享这个数据就可以了,整个过程只需要执行一次内存拷贝,提高了效率”,这里面说的共享数据是怎么一回事呢?这个linux IPC里面的内存共享有什么区别?小弟菜鸟,望大神赐教!!

Re: 罗升阳 2011-12-17 20:42发表 [回复]
回复yunzhongyuekelly:内存共享和进程间通信虽然讲的都是两个进程之间的事情,但是它们是有区别的,进程间通信有信息同步的意思,而共享内存在两个进程间的信息是不同步的,即一个进程改变了共享内存的内容,但是另外一个进程是不知道的,它一般要配合一种进程间通信机制来告诉对方,共享内存的内容发生改变了。
5楼 wqtian2010 2011-11-21 20:35发表 [回复]
楼主,建立128K内存映射:mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);是不是意味着binder传输数据大小的上限是128K,超过这个大小传输数据会失败?如果不是128K,那Binder最大一次能传多大数据?

Re: 罗升阳 2011-11-21 21:54发表 [回复]
回复wqtian2010:128k是Service Manager的行为,Binder驱动限制的最大值为4M,如果大于4M,就会截取为4M,具体你可以看一下Binder驱动程序里面的binder_mmap函数。

Re: wqtian2010 2011-11-22 09:17发表 [回复]
回复Luoshengyang:我想请教另一个问题,应用使用Intent传输数据是使用什么机制,在底层是不是也是通过Binder来实现的?我做应用的时候用Intent传输200多k的图片时,发现会传输失败,log里面有out of memory错误。使用Intent一次能传输多大的数据?

Re: 罗升阳 2011-11-22 21:55发表 [回复]
回复wqtian2010:如果这个Intent是两个进程之间传输的话,最终是通过Binder来传输的。每个应用程序进程在启动的时候,会向Binder驱动程序申请大约1M的内存空间,这个数字的定义如下:
#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
所以理论上传200K的图片是没有问题的。这个问题我觉得最好是看一下"out of memory"这个log是在哪里输出来的,然后再看一下到底是哪里出了问题。Intent的最终会通过它的writeToParcel成员函数把数据都写到一个Parcel里面去,然后再传输,可以沿着这条路分析下去。
4楼 flxue 2011-11-08 00:32发表 [回复]
mark一下
3楼 lowinrain 2011-09-01 09:44发表 [回复]
您好,看了你的文章收益颇深,不过有个问题想请教楼主,native的service通常在主线程里给binder driver下了BC_ENTER_LOOPER命令,告诉driver该线程已经进入主循环,可以接收数据。Client是如何接收数据的?通常client都会把自己的IBinder类型注册给server知道,可是我没有找到client是在哪里告诉driver进入循环。

Re: 罗升阳 2011-09-01 10:11发表 [回复]
回复lowinrain:Client不像Server一样有一个专门的主循环在等待数据的到来,不过,当Client调用Server的接口并且要求返回结果时,的确是是会有一个循环在检查Server的返回结果,可以参考一下后面一篇文章《Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析》,在IPCThreadState::waitForResponse这一步,会且一个循环在检查远程调用结果的返回。

Re: lowinrain 2011-09-01 12:06发表 [回复]
回复Luoshengyang:呵呵,楼主说的很正确,可是不是我想提的问题哦
以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的数据?

Re: 罗升阳 2011-09-01 23:15发表 [回复]
回复lowinrain:这位兄弟看来对Android的Binder进程间通信机制也很有研究啊,呵呵
Re: 罗升阳 2011-09-01 23:02发表 [回复]
回复lowinrain:这个问题问得很好~
Java层已经为我们封装好了一切,当我们在启动一个Android应用程序的时候,一般会通过Process.start接口启动一个新的进程,新的进程在启动的时候,会通过JNI层,最覆盖调用运行时库的ProcessState::startThreadPool函数启动一个线程池,线程池里面的线程最终都会进入到IPCThreadState::joinThreadPool函数中,这个函数会有一个无限循环在和Binder驱动程序进行交互,直到线程退出。
因此,在Java层,我们只要实现自己的Binder对象就可以了,其它的进程只要拿到这个Binder对象的远程接口,就可以调用它;但是如果我们在C++层实现自己的Binder对象,就要自己手动地调用ProcessState::startThreadPool来进入一个无限循环来和Binder驱动程序交互,以便获得Client的请求。
有时间的话,我再写一篇文章来描述一下这个过程。

Re: lowinrain 2011-09-02 11:10发表 [回复]
回复Luoshengyang:呵呵,说不上研究,小巫见大巫了,没弄过java的部分,看来楼主会这个问题,给个call stack来?java的code对于我来说太陌生,无从下手啊

Re: 罗升阳 2011-09-09 11:00发表 [回复]
回复lowinrain:你可以看一下后面一篇文章《Android应用程序进程启动过程的源代码分析》

Re: Ritter_Liu 2013-09-05 22:28发表 [回复]
回复Luoshengyang:此观二人对白,受益匪浅。。。
2楼 hui05504 2011-08-03 15:57发表 [回复]
楼主,bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0); 
为什么会对应Binder驱动程序的binder_mmap函数?

Re: 罗升阳 2011-08-03 21:16发表 [回复]
回复hui05504:这个不是一两句话能说清楚的,建议你去看看Linux文件系统和驱动程序相关的资料。mmap和open都是系统调用,它们最终经过文件系统层到达驱动层,而驱动程序在初始化时会注册相关的接口到驱动层,从而上层的系统调用可以调用到它们注册的函数里面去。
1楼 Mamba2016 2011-07-22 10:51发表 [回复] [引用] [举报]
不错...关于handle是不是按服务的注册顺序,加1呀?

Re: 罗升阳 2011-07-22 11:13发表 [回复] [引用] [举报]
回复kobeyxyx:这个不一定是按注册顺序加1的。在Binder驱动程序的binder_get_ref_for_node函数里面,有这样一段逻辑:
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值

浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路相关推荐

  1. 浅谈Service Manager成为Android进程间通信(IPC)机制Binder守护进程之路(1)

    上一篇文章Android进程间通信(IPC)机制Binder简要介绍和学习计划简要介绍了Android系统进程间通信机制Binder的总体架构,它由Client.Server.Service Mana ...

  2. 深入理解 Android 的 IPC 机制--------Binder

    在android中,,应用程序虽然是以独立的进程来运行的,但相互之间还是需要通信..比如,,我们的应用程序 和后台服务往往会运行不同的进程当中,,各自有这独立内存地址空间,,,但是又要彼此互相合作,, ...

  3. Android的IPC机制Binder

    第一部分 Binder的组成  1.1 驱动程序部分驱动程序的部分在以下的文件夹中: Java代码  kernel/include/linux/binder.h kernel/drivers/andr ...

  4. 浅谈 unix, linux, ios, android 区别和联系

    浅谈 unix, linux, ios, android 区别和联系 网上的答案并不是很好,便从网上整理的相对专业的问答. 1.UNIX 和 Linux UNIX 操作系统(尤尼斯), 是一个强大的多 ...

  5. Android进程间通信之一:Binder机制学习

    Binder机制学习 Binder驱动 Binder核心API Linux 使用两级保护机制:0 级供系统内核使用,3 级供用户程序使用. Linux 下的传统 IPC 通信原理 Linux 下的传统 ...

  6. 多线程之旅之四——浅谈内存模型和用户态同步机制

     用户态下有两种同步结构的 volatile construct: 在简单数据类型上原子性的读或者写操作   interlocked construct:在简单数据类型上原子性的读和写操作 (在这里还 ...

  7. 浅谈专有云MQ存储空间的清理机制

    简介:浅谈专有云MQ存储空间的清理机制 在近⼀年的项⽬保障过程中,对专有云MQ产品的存储⽔位清理模式⼀直存疑,总想一探究竟但又苦于工作繁忙.精力有限,直到最近⼀次项⽬保障过程中再次出现了类似的问题,⼤ ...

  8. 浅谈V8引擎中的垃圾回收机制

    浅谈V8引擎中的垃圾回收机制 这篇文章的所有内容均来自 朴灵的<深入浅出Node.js>及A tour of V8:Garbage Collection,后者还有中文翻译版V8 之旅: 垃 ...

  9. 浅谈实时对战网络游戏的同步机制

    浅谈实时对战网络游戏的同步机制 重要的性能指标 三种不同方向的技术实现介绍 非帧状态同步 帧指令同步 帧状态同步 三种同步方式的对比 帧状态同步和ECS架构 实时对战游戏,相信大家都不陌生,一些经典的 ...

最新文章

  1. 解决 The mysql extension is deprecated and will be r
  2. WebView的截屏实现
  3. TensorFlow 笔记2--MNIST手写数字分类
  4. Redis开发:hash存储自定义Java对象及value的序列化器设置
  5. 关于mysql中外键关联的一些个人理解
  6. 数据库的几个概念:主键,外键,索引,唯一索引
  7. NameError: name 'long' is not defined
  8. Java LinkedList公共布尔boolean offerLast(Object o)方法(带示例)
  9. 弹性理论法研究桩基受力计算公式_竖向荷载下群桩的承载力分析
  10. 计算机网络课程设计之简单 Web Server 程序的设计与实现
  11. org.xml.sax.SAXParseException: Failed to read schema document错误的完美解决方法 以及 Spring如何加载XSD文件
  12. java varargs_Java中方法重载中的Varargs
  13. C#控制台程序取得INSOYA视频区的视频的真实URL,视频标题,发布时间集合。
  14. activity绑定service
  15. qt视频教程qt入门教程嵌入式linux应用开发qt编程开发,QT项目实战教程-Qt应用项目实例开发视频教程...
  16. 修复win10右键卡死鼠标转圈
  17. 公司-瑞幸咖啡:瑞幸咖啡
  18. 理工附中2021年高考成绩查询,人大附中、理工附中、101中学、十二中等5区10校高考成绩汇总!...
  19. JavaWeb购物系统
  20. js ios调用ios方法_通过iOS 13的模式演示调用生命周期方法

热门文章

  1. [最新答案V0.4版]微软等数据结构+算法面试100题[第41-60题答案]
  2. c++学习笔记之类和对象的进阶
  3. Python科学画图小结
  4. 超强包管理器:Anaconda 安装
  5. 用C语言扩展Python的功能的实例
  6. 量子遗传算法原理与MATLAB仿真程序
  7. 二十万字C/C++、嵌入式软开面试题全集宝典八
  8. 科大星云诗社动态20210202
  9. 这 28 张精炼图,将吴恩达的 deeplearning.ai 总结得恰到好处!
  10. 吴恩达《卷积神经网络》精炼笔记(3)-- 目标检测