第一节 Android Binder


第一节 Android Binder
2010年07月19日
  转自:http://gmier.com/node/11 通常来说,Binder是Android系统中的内部进程通讯(IPC)之一。在Android系统中共有三种IPC机制,分别是:
  -标准Linux Kernel IPC接口
  -标准D-BUS接口
  -Binder接口
  尽管Google宣称Binder具有更加简洁、快速,消耗更小内存资源的优点,但并没有证据表明D-BUS就很差。实际上D-BUS可能会更合适些,或许只是当时Google并没有注意到它吧,或者Google不想使用GPL协议的D-BUS库。我们不去探究具体的原因了,你只要清楚Android系统中支持了多个IPC接口,而且大部分程序使用的是我们并不熟悉的Binder接口。
  Binder是OpenBinder的Google精简实现,它包括一个Binder驱动程序、一个Binder服务器及Binder客户端(?)。这里我们只要介绍内核中的Binder驱动的实现。
  对于Android Binder,它也可以称为是Android系统的一种RPC(远程过程调用)机制,因为Binder实现的功能就是在本地"执行"其他服务进程的功能的函数调用。不管是IPC也好,还是RPC也好,我们所要知道的就是Android Binder的功能是如何实现的。
  Openbinder介绍
  2.1.1 Android Binder协议
  Android 的Binder机制是基于OpenBinder (http://www.angryredplanet.com/~hackbod/openbinder /docs/html/BinderIPCMechanism.html)来实现的,是一个OpenBinder的Linux实现。
  Android Binder的协议定义在binder.h头文件中,Android的通讯就是基于这样的一个协议的。
  * Binder Type
  (描述binder type的功能)
  Android定义了五个(三大类)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),
  };
  * Binder Object
  进程间传输的数据被称为Binder对象(Binder Object),它是一个flat_binder_object,定义如下:
  struct flat_binder_object {
  /* 8 bytes for large_flat_header. */
  unsigned long type;
  unsigned long flags;
  /* 8 bytes of data. */
  union {
  void *binder; /* local object */
  signed long handle; /* remote object */
  };
  /* extra data associated with local object */
  void *cookie;
  };
  其中,类型字段描述了Binder 对象的类型,flags描述了传输方式,比如同步、异步等。
  enum transaction_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 */
  };
  传输的数据是一个复用数据联合体,对于BINDER类型,数据就是一个binder本地对象,如果是HANDLE类型,这数据就是一个远程的handle 对象。该如何理解本地binder对象和远程handle对象呢?其实它们都指向同一个对象,不过是从不同的角度来说。举例来说,假如A有个对象X,对于 A来说,X就是一个本地的binder对象;如果B想访问A的X对象,这对于B来说,X就是一个handle。因此,从根本上来说handle和 binder都指向X。本地对象还可以带有额外的数据,保存在cookie中。
  Binder对象的传递是通过binder_transaction_data来实现的,即Binder对象实际是封装在binder_transaction_data结构体中。
  * binder_transaction_data
  这个数据结构才是真正要传输的数据。它的定义如下:
  struct binder_transaction_data {
  /* The first two are only used for bcTRANSACTION and brTRANSACTION,
  * identifying the target and contents of the transaction.
  */
  union {
  size_t handle; /* target descriptor of command transaction */
  void *ptr; /* target descriptor of return transaction */
  } target;
  void *cookie; /* target object cookie */
  unsigned int code; /* transaction command */
  /* General information about the transaction. */
  unsigned int flags;
  pid_t sender_pid;
  uid_t sender_euid;
  size_t data_size; /* number of bytes of data */
  size_t offsets_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 */
  const void *buffer;
  /* offsets from buffer to flat_binder_object structs */
  const void *offsets;
  } ptr;
  uint8_t buf[8];
  } data;
  };
  结构体中的数据成员target是一个复合联合体对象,请参考前面的关于binder本地对象及handle远程对象的描述。code是一个命令,描述了请求Binder对象执行的操作。
  * 对象的索引和映射
  Binder中的一个重要概念就是对象的映射和索引。就是要把对象从一个进程映射到另一个进程中,以实现线程迁移的概念。前面描述过Binder的一个重要概念是进程/线程迁移,即当一个进程需要同另一个进程通信时,它可以"迁移"远程的进程/线程到本地来执行。对于调用进程来说,看起来就像是在本地执行一样。这是Binder与其他IPC机制的不同点或者说是优点。当然迁移的工作是由Binder驱动来完成的,而实现的基础和核心就是对象的映射和索引。
  Binder中有两种索引,一是本地进程地址空间的一个地址,另一个是一个抽象的32位句柄(HANDLE),它们之间是互斥的:所有的进程本地对象的索引都是本地进程的一个地址(address, ptr, binder),所有的远程进程的对象的索引都是一个句柄(handle)。对于发送者进程来说,索引就是一个远端对象的一个句柄,当Binder对象数据被发送到远端接收进程时,远端接受进程则会认为索引是一个本地对象地址,因此从第三方的角度来说,尽管名称不同,对于一次Binder调用,两种索引指的是同一个对象,Binder驱动则负责两种索引的映射,这样才能把数据发送给正确的进程。
  * BinderDriverCommandProtocol
  Binder驱动的命令协议(BC_命令),定义了Binder驱动支持的命令格式及数据定义(协议)。不同的命令所带有的数据是不同的。Binder的命令由binder_write_read数据结构描述,它是ioctl命令(BINDER_WRITE_READ)的参数。
  struct binder_write_read {
  signed long write_size; /* bytes to write */
  signed long write_consumed; /* bytes consumed by driver */
  unsigned long write_buffer;
  signed long read_size; /* bytes to read */
  signed long read_consumed; /* bytes consumed by driver */
  unsigned long read_buffer;
  };
  对于写操作,write_buffer包含了一系列请求线程执行的Binder命令;对于读(返回)操作,read_buffer包含了一系列线程执行后填充的返回值。
  Binder命令(BC_)用于BINDER_WRITE_READ的write操作。
  Binder的BC的命令格式是:| CMD | Data... |
  Binder Commands CMD Data Format Notes
  BC_TRANSACTION
  BC_REPLY binder_transaction_data 
  BC_ACQUIRE_RESULT
  BC_ATTEMPT_ACQUIRE Not implement
  BC_FREE_BUFFER data_ptr ptr to transaction data received on a read
  BC_INCREFS
  BC_ACQUIRE
  BC_RELEASE
  BC_DECREFS int target descriptor
  BC_INCREFS_DONE
  BC_ACQUIRE_DONE node_ptr | cookie 
  BC_REGISTER_LOOPER
  BC_ENTER_LOOPER
  BC_EXIT_LOOPER No parameters
  BC_REQUEST_DEATH_NOTIFICATION target_ptr | cookie 
  BC_DEAD_BINDER_DONE cookie 
  * BinderDriverReturnProtocol
  Binder驱动的响应(返回,BR_)协议,定义了Binder命令的数据返回格式。同Binder命令协议一样,Binder驱动返回协议也是通过BINDER_WRITE_READ ioctl命令实现的,不同的是它是read操作。
  Binder BR的命令格式是:| CMD | Data... |
  Binder BR 命令 CMDS Data Format Notes
  BR_ERROR int Error code
  BR_OK
  BR_NOOP
  BR_SPAWN_LOOPER No parameters
  BR_TRANSACTION
  BR_REPLY binder_transaction_data the received command
  BR_ACQUIRE_RESULT
  BR_ATTEMPT_ACQUIRE
  BR_FINISHED Not implement
  BR_DEAD_REPLY The target of the last transaction is no longer with us.
  bcTRANSACTION or bcATTEMPT_ACQUIRE
  BR_TRANSACTION_COMPLETE No parameters...
  Always refers to the last transaction requested (including replies).
  Note that this will be sent even for asynchronous transactions
  BR_INCREFS
  BR_ACQUIRE
  BR_RELEASE
  BR_DECREFS target_ptr | cookie 
  BR_DEAD_BINDER
  BR_CLEAR_DEATH_NOTIFICATION_DONE cookie 
  BR_FAILED_REPLY The the last transaction
  (either a bcTRANSACTION or a bcATTEMPT_ACQUIRE) failed
  (e.g. out of memory).
  * Android Binder 进程/线程模型
  (描述Android的进程模型)
  2.1.2 驱动接口
  Android Binder设备驱动接口函数是
  device_initcall(binder_init);
  我们知道一般来说设备驱动的接口函数是module_init和module_exit,这么做是为了同时兼容支持静态编译的驱动模块(buildin)和动态编译的驱动模块(module)。但是Android的Binder驱动显然不想支持动态编译的驱动,如果你需要将Binder驱动修改为动态的内核模块,可以直接将device_initcall修改为module_init,但不要忘了增加module_exit的驱动卸载接口函数。
  * binder_init
  初始化函数首先使用proc_mkdir创建了一个Binder的proc文件系统的根节点(binder_proc_dir_entry_root,/proc/binder),并为binder创建了binder proc节点(binder_proc_dir_entry_proc,/proc/binder/proc),注意不要混淆Linux Proc和Binder Proc。
  然后Binder驱动使用misc_register把自己注册为一个Misc设备(/dev/misc/binder)。
  最后,如果驱动成功的创建了/proc/binder根节点,就调用create_proc_read_entry创建只读proc文件:/proc /binder/state,/proc/binder/stats,/proc/binder/transactions,/proc/binder /transaction_log,/proc/binder/failed_transaction_log。
  这个初始化函数有个小小的问题,它没有判断Misc设备是否注册成功了,如果注册失败了,那么Binder就不能正常工作了,因此这里应该有个错误处理流程。
  2.1.3 Binder核心数据
  在进一步介绍Binder驱动之前,我们有必要了解一下Binder的核心数据。
  * binder_proc
  struct binder_proc {
  struct hlist_node proc_node;
  struct rb_root threads;
  struct rb_root nodes;
  struct rb_root refs_by_desc;
  struct rb_root refs_by_node;
  int pid;
  struct vm_area_struct *vma;
  struct task_struct *tsk;
  struct files_struct *files;
  struct hlist_node deferred_work_node;
  int deferred_work;
  void *buffer;
  ptrdiff_t user_buffer_offset;
  struct list_head buffers;
  struct rb_root free_buffers;
  struct rb_root allocated_buffers;
  size_t free_async_space;
  struct page **pages;
  size_t buffer_size;
  uint32_t buffer_free;
  struct list_head todo;
  wait_queue_head_t wait;
  struct binder_stats stats;
  struct list_head delivered_death;
  int max_threads;
  int requested_threads;
  int requested_threads_started;
  int ready_threads;
  long default_priority;
  };
  binder_proc用于保存调用binder的各个进程或线程信息,比如线程id、进程id、binder状态信息等。
  (各个数据的意义?)
  * binder_node
  struct binder_node {
  int debug_id;
  struct binder_work work;
  union {
  struct rb_node rb_node;
  struct hlist_node dead_node;
  };
  struct binder_proc *proc;
  struct hlist_head refs;
  int internal_strong_refs;
  int local_weak_refs;
  int local_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;
  int min_priority : 8;
  struct list_head async_todo;
  };
  * binder_thread
  2.1.4 用户接口
  驱动程序的一个主要同能就是向用户空间的程序提供操作接口,这个接口是标准的,对于Android Binder驱动,包含的接口有:
  -Proc接口(/proc/binder)
  . /proc/binder/state
  . /proc/binder/stats
  . /proc/binder/transactions
  . /proc/binder/transaction_log
  . /proc/binder/failed_transaction_log
  . /proc/binder/proc/
  - 设备接口(/dev/binder)
  . binder_open
  . binder_release
  . binder_flush
  . binder_mmap
  . binder_poll
  . binder_ioctl
  这些内核接口函数是在驱动程序的初始化函数(binder_init)中初始化的,感兴趣的读者可以阅读相关函数的实现代码,很简单明了,一共才十几行的代码。  * binder_open 通常来说,驱动程序的open函数是用户调用驱动接口来使用驱动功能的第一个函数,称为入口函数。同其他驱动一样,对于Android驱动,任何一个进程及其内的所有线程都可以打开一个binder设备。首先来看看Binder驱动是如何打开设备的。
  首先,binder驱动分配内存以保存binder_proc数据结构。
  然后,binder填充binder_proc数据(初始化),增加当前线程/进程的引用计数并赋值给tsk
  get_task_struct(current);
  proc->tsk = current;
  初始化binder_proc的队列及默认优先级
  INIT_LIST_HEAD(&proc->todo);
  init_waitqueue_head(&proc->wait);
  proc->default_priority = task_nice(current);
  增加BINDER_STAT_PROC的对象计数,并把创建的binder_proc对象添加到全局的binder_proc哈希列表中,这样任何一个进程就都可以访问到其他进程的binder_proc对象了。
  binder_stats.obj_created[BINDER_STAT_PROC]++;
  hlist_add_head(&proc->proc_node, &binder_procs);
  把当前进程/线程的线程组的pid(pid指向线程id)赋值给proc的pid字段,可以理解为一个进程id(thread_group指向线程组中的第一个线程的task_struct结构)。同时把binder_proc对象指针赋值给filp的private_data对象保存起来。
  proc->pid = current->group_leader->pid;
  INIT_LIST_HEAD(&proc->delivered_death);
  filp->private_data = proc;
  最后,在bindr proc目录中创建只读文件(/proc/binder/proc/$pid)用来输出当前binder proc对象的状态。
  这里要注意的是,proc->pid字段,按字面理解它应该是保存当前进程/线程的id,但实际上它保存的是线程组的pid,也就是线程组中的第一个线程的pid(等于tgid,进程id)。
  这样当一个进程或线程打开一个binder设备时,驱动就会在内核中为其创建binder_proc结构来保存打开此设备的进程/线程信息。
  * binder_release
  binder_release是一个驱动的出口函数,当进程退出(exit)时,进程需要显示或隐式的调用release函数来关闭打开的文件。Release函数一般来清理进程的内核数据,释放申请的内存。
  Binder驱动的release函数相对比较简单,它删除open是创建的binder proc文件,然后调度一个workqueue来释放这个进程/线程的binder_proc对象(BINDER_DEFERRED_RELEASE)。这里要提的一点就是使用workqueue(deffered)可以提高系统的响应速度和性能,因为Android Binder的release及flush等操作是一个复杂费事的任务,而且也没有必要在系统调用里完成它,因此最好的方法是延迟执行这个费时的任务。其实在中断处理函数中我们经常要把一些耗时的操作放到底半部中处理(bottom half),这是一样的道理。
  当然真正的释放工作是在binder_deferred_release函数里完成的,后面在做详尽的介绍。
  workqueue是Linux系统中延迟执行任务的一种机制
  * binder_flush
  flush操作在关闭一个设备文件描述符拷贝时被调用,Binder的flush函数十分简单,它只是简单的调度一个workqueue执行BINDER_DEFERRED_FLUSH操作。
  * binder_mmap
  mmap(memory map)用于把设备内存映射到用户进程地址空间中,这样就可以像操作用户内存那样操作设备内存。(还有一种说法,mmap用于把用户进程地址空间映射到设备内存上,尽管说法不同,但是说的是同一个事情)
  Binder设备对内存映射是有些限制的,比如binder设备最大能映射4M的内存区域;binder不能映射具有写权限的内存区域。
  不同于一般的设备驱动,大多的设备映射的设备内存是设备本身具有的,或者在驱动初始化时由vmalloc或kmalloc等内核内存函数分配的,Binder的设备内存是在mmap操作时分配的,分配的方法是先在内核虚拟映射表上获取一个可以使用的区域,然后分配物理页,并把物理页映射到获取的虚拟空间上。由于设备内存是在mmap操作中实现的,因此每个进程/线程只能做映射操作一次,其后的操作都会返回错误。
  具体来说,binder_mmap首先检查mmap调用是否合法,即是否满足binder内存映射的条件,主要检查映射内存的大小、flags,是否是第一次调用。
  if ((vma->vm_end - vma->vm_start) > SZ_4M)
  vma->vm_end = vma->vm_start + SZ_4M;
  if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
  ...
  }
  vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;
  if (proc->buffer) {
  ...
  }
  然后,binder驱动从系统申请可用的虚拟内存空间(注意不是物理内存空间),这是通过get_vm_area内核函数实现的:(get_vm_area是一个内核,功能是在内核中申请并保留一块连续的内核虚拟内存空间区域)
  area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
  proc->buffer = area->addr;
  proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
  然后根据请求映射的内存空间大小,分配binder核心数据结构binder_proc的pages成员,它主要用来保存指向申请的物理页的指针。
  proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
  proc->buffer_size = vma->vm_end - vma->vm_start;
  一切都准备就绪了,现在开始分配物理内存(page)了。这是通过binder驱动的帮助函数binder_update_page_range来实现的。尽管名字上称为update page,但实际上它是在分配物理页并映射到刚才保留的虚拟内存空间上。在这里,Binder使用alloc_page分配页面,使用 map_vm_area为分配的内存做映射关系,使用vm_insert_page把分配的物理页插入到用户vma区域。
  *page = alloc_page(GFP_KERNEL | __GFP_ZERO);
  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);
  user_page_addr = (uintptr_t)page_addr + proc->user_buffer_offset;
  ret = vm_insert_page(vma, user_page_addr, page[0]);
  注:alloc_page, map_vm_area和vm_insert_page都是Linux内核中内存相关函数。
  当然,这里会使用到的核心数据结构binder_proc,用到的主要域是
  buffer 记录binder_proc的内核虚拟地址的首地址
  buffer_size 记录binder_proc的虚拟地址的大小
  user_buffer_offset 记录binder_proc的用户地址偏移,即用户进程vma地址与binder申请的vma地址的偏差
  pages 记录指向binder_proc物理页(struct page)的指针(二维指针)
  free 
  free_async_space 
  files 记录进程的struct file_struct 结构
  vma 记录用户进程的vma结构
  显然,这些都同内存相关。
  * binder_poll
  poll函数是非阻塞型IO(select,poll调用)的内核驱动实现,所有支持非阻塞IO操作的设备驱动都需要实现poll函数。
  Binder的poll函数仅支持设备是否可非阻塞的读(POLLIN),这里有两种等待任务,一种是一种是proc_work,另一种是thread_work。同其他驱动的poll实现一样,这里也是通过调用poll_wait函数来实现的,这里就不多做叙述了。
  poll_wait(filp, &thread->wait, wait);
  这里需要明确提的一点就是这里调用了下面的函数来取得当前进程/线程的thread对象:
  thread = binder_get_thread(proc);
  特别要指出的,也是很重要的一点,就是这个函数实际上在为服务进程的线程池创建对应的thread对象。后面还会详细讲解这个函数。
  * binder_ioctl
  这个函数是Binder的最核心部分,Binder的功能就是通过ioctl命令来实现的。Binder的ioctl命令共有7个,定义在ioctl.h头文件中:
  #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
  #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, int64_t)
  #define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t)
  #define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, int)
  #define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int)
  #define BINDER_THREAD_EXIT _IOW('b', 8, int)
  #define BINDER_VERSION _IOWR('b', 9, struct binder_version)
  首先要说明的是BINDER_SET_IDLE_TIMEOUT 和 BINDER_SET_IDLE_PRIORITY 在目前的Binder驱动中没有实现。
  这里最重要的就是BINDER_WRITE_READ命令,它是Binder驱动核心的核心,Binder IPC机制主要是通过这个命令来实现的。下面我们首先来介绍简单的用于设置Binder驱动参数的几个ioctl命令,最后着重讲述BINDER_WRITE_READ命令。
  1. BINDER_SET_MAX_THREADS
  这个ioctl命令用于设置进程的Biner对象所支持的最大线程数。设置的值保存在binder_proc结构的max_threads成员里。
  2. BINDER_SET_CONTEXT_MGR
  在这里会引入Binder的另一个核心数据binder_node。从功能上看,只有一个进程/线程能成功设置 binder_context_mgr_node对象,这个进程被称为Context Manager(context_mgr)。当然,也只有创建binder_context_mgr_node对象的Binder上下文管理进程/线程才有权限重新设置这个对象。进程的权限(cred->euid)保存在binder_context_mgr_uid对象里。
  binder_context_mgr_uid = current->cred->euid;
  从接口的角度来说,这是一个进程想要成为一个Context Manager的唯一接口。一个Context Manager进程需要为binder_proc创建一个binder_node类型的节点。节点是通过binder_new_node函数来创建的,我们在后面在详细讲解这个函数。节点创建成功后内核会初始化节点的部分数据(weak_ref和strong_ref)。
  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;
  binder_proc成员node是binder_node的根节点,这是一棵红黑树(一种平衡二叉树)。
  现在来看看binder_new_node函数,它首先根据规则找到第一个页节点作为新插入的节点的父节点(规则就是binder_node的指针对象,后面还会遇到)。
  while (*p) {
  parent = *p;
  node = rb_entry(parent, struct binder_node, rb_node);
  if (ptr ptr)
  p = &(*p)->rb_left;
  else if (ptr > node->ptr)
  p = &(*p)->rb_right;
  else
  return NULL;
  }
  找到节点了,那么调用内核函数创建并插入节点吧。
  node = kzalloc(sizeof(*node), GFP_KERNEL);
  binder_stats.obj_created[BINDER_STAT_NODE]++;
  rb_link_node(&node->rb_node, parent, p);
  rb_insert_color(&node->rb_node, &proc->nodes);
  注:rb_link_node和rb_insert_color都是内核红黑树函数,rb_link_node是一个内联函数,它把新节点插入到红黑树中的指定父节点下。rb_insert_color节把已经插入到红黑树中的节点调整并融合到红黑树中(参考根据红黑树规则)。
  插入完成后,做最后的初始化工作,这里着重说明两点,一是把binder_proc对象指针保存在binder_node对象里,二是初始化node对象的链表头指针。  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); 描述Context Manager的作用是什么呢?
  3. BINDER_THREAD_EXIT
  通过调用binder_free_thread终止并释放binder_thread对象及其binder_transaction事务。
  (需要更新,详细描述函数功能)
  biner_thread会在后面说明,功能是什么?
  4. BINDER_VERSION
  读取当前Binder驱动支持的协议版本号。
  5. BINDER_WRITE_READ
  前面提到,这个ioctl命令才是Binder最核心的部分,Android Binder的IPC机制就是通过这个接口来实现的。我们在这里来介绍binder_thread对象,其实在前面已经见到过了,但是因为它与这个接口更加紧密,因此我们把它拿到这里来介绍。
  每一个进程的binder_proc对象都有一个binder_thread对象(proc->threads.rb_node节点),进程/线程的id(pid)就保存在binder_thread结构的pid成员里。Binder_thread对象是在binder_get_thread函数中创建的,ioctl函数在入口处会调用它来取得或创建binder_thread对象
  thread = binder_get_thread(proc);
  binder_thread对象保存在binder_proc对象的thread成员里,同binder_node一样,它是一棵红黑树。首先我们先来看一下binder_get_thread函数。这个函数还是比较简单的,它遍历thread树找到同当前进程相关的binder_thread对象,判断条件就是当前进程/线程的pid要等于thread对象里记录的pid,看下面的代码:
  while (*p) {
  parent = *p;
  thread = rb_entry(parent, struct binder_thread, rb_node);
  if (current->pid pid)
  p = &(*p)->rb_left;
  else if (current->pid > thread->pid)
  p = &(*p)->rb_right;
  else
  break;
  }
  如果找到了binder_thread对象,就直接返回该对象。如果没有找到,就说明当前进程/线程的binder_thread对象还没有创建,创建一个新的binder_thread节点并插入到红黑树中,返回这个新创建的binder_thread对象。当然,这里还要对binder_thread 对象最一个初始化工作
  thread = kzalloc(sizeof(*thread), GFP_KERNEL);
  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;
  在介绍BINDER_WRITE_READ之前,我们先来看一下接口的参数,我们知道,每一个ioctl命令都可以有一个数据参数,BINDER_WRITE_READ的参数是一个binder_write_read类型的数据结构,它描述了可读写的数据,BINDER_WRITE_READ就是根据它的具体内容来做写操作或是读操作。
  struct binder_write_read {
  signed long write_size; /* bytes to write */
  signed long write_consumed; /* bytes consumed by driver */
  unsigned long write_buffer;
  signed long read_size; /* bytes to read */
  signed long read_consumed; /* bytes consumed by driver */
  unsigned long read_buffer;
  };
  具体的读写操作是通过两个核心函数来实现的,分别是binder_thread_write和binder_thread_read。
  if (bwr.write_size > 0) {
  ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
  }
  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);
  }
  Binder协议的命令(BC_和BR_)就是通过这个BINDER_WRITE_READ ioctl接口实现的,下面我们具体来看一下这些命令的实现。
  (未完待续......)

猜你喜欢

转载自ksf83ksf.iteye.com/blog/1362015