Binder Driver

Table of Contents

1. Binder Driver

Binder driver 中几个最重要的数据结构:

  • binder_proc
  • binder_node
  • binder_ref
  • binder_thread
  • binder_transaction
  • binder_transaction_data
  • binder_work

1.1. binder_proc

binder_proc 是 binder driver 中处于最顶层的数据结构, 它代表了一个与 binder driver 打交道的进程, 任意一个和 binder 打交道的进程, 不管是 server 还是 client, 在 kernel 都有且只有一个 binder_proc 结构与之对应.

binder_proc 主要成员包括:

  • files 与进程的 task_struct 中的 files 相同, 通过 binder 传递 fd (文件描述符)时需要使用该成员
  • VMA 与进程 task_struct 中的 mm 相同, 分配/释放 binder buffer 时需要使用该成员
  • nodes 该进程"拥有"的 binder_node, 可以暂且把一个 binder_node 看作进程对外提供的一个 service, 所以 nodes 相当于该进程对外提供的所有 service.
  • threads 进程中所有的 binder_thread, 这些 binder_thread 是一些真正的 worker thread, binder_node 所代表的 service 就是在这些线程中执行任务的.
  • buffers, free_buffers, allocated_buffers 每个进程有固定大小的 buffer (1M-8K), 用来保存 binder 调用时的参数和返回值
  • refs_by_desc, refs_by_node 这两个结构体是两根红黑树, 树结点的 value 都是 binder_ref, 但对于 refs_by_desc 树, 结点的 key 是 binder_ref.desc, 对于 refs_by_node, key 是 binder_node

1.1.1. binder_proc 结构体的初始化

binder_proc 代表一个与 binder 打交道的进程, 不论 binder server 或是 binder client, 都有且仅有一个 binder_proc 与之对应.

binder_proc 的初始化:
ProccessState::self()
  new ProcessState()
    open /dev/binder
      // binder.c
      binder_open()
        kmalloc(proc)
          filp->private_data=proc

filp->private_data 是进程私有数据, 每当进程通过系统调用进入 kernel 时, driver 代码总是可以通过 task_struct->filp->private_data 轻松找到该进程对应的 binder_proc

1.1.2. binder_proc 的清除

当进程终止时, binder_proc 会被 driver 清除.

当进程终止时 (正常退出或因为信号异常退出), kernel 会负责关闭该进程打开的所有文件描述符, 因为进程通过打开过/dev/binder, 所以 kernel 会关闭该文件描述符, 这个动作会导致 driver 的 binder_release 函数被调用:

binder_release()
  binder_proc proc = filp->private_data;
  // service manager 进程挂掉了...
  if (binder_context_mgr_node->proc==proc):
    binder_context_mgr_node=NULL;
  // 释放该进程所有的 binder_thread
  // 释放该进程所有 binder_node,同时通知使用这些 binder_node (service) 的所
  // 有 binder_ref (client):  这个 binder 挂掉了 (binder.linkToDeath)
  foreach (node: proc->nodes):
    // 从 proc->nodes 中删除该 node
    rb_erase(node, proc->nodes)
    // 如果该 node 没有任何 binder_ref 使用它 (service 没有任何 client)
    if (hlist_empty (node->refs)):
      kfree(node);
    else:
      foreach (ref:node->refs):
        // linkToDeath
        if (ref->death):
          // 给 ref->proc->todo 添加一个 work(通知它 node 已经 dead),
          // 并唤醒 ref 所在的进程
          list_add_tail(ref->death->...)
          wake_up_interruptible(ref->proc->wait);
  // 释放所有的 binder_ref
  // 释放所有的 binder_buffer
  // 最后释放 binder_proc 结构体本身
  kfree(proc)

1.2. binder_node

从用户的角度看,binder_node 代表一个 service, 它与 c++ binder 的 BBinder 对象有一一对应关系.

binder_node 的主要成员有:

  • binder_proc proc

    表示这个 binder_node 属于哪个进程. 即这个 service 是哪个进程提供的.

  • cookie

    这个 binder_node 对应的用户空间的 c++ BBinder 对象的地址

  • refs

    还记得前面提到的 binder_proc 被清除时如何处理 linkToDeath 的么?

binder_node 与 binder_proc 的关系:

             +----------------------+
+----------+>+  server binder_proc  +-<------------+
|            +-----------+----------+              |
|              +---------+------------+            |
|              |     rb_root nodes    |            |
|              +----------------------+            |
|                 --/      \--                     |
|                --/        \--                    |
|               --/          \--                   |
|               -/            \-                   |
|     +------------+          +------------+       |
|     | rb_node 1  |          |  rb_node 2 |       |
|     +------------+          +------------+       |
+-----+  proc      |          |    proc    +-------+
      +------------+          +------------+

1.3. binder_ref

从用户的角度看, binder_ref 代表一个 client, 它与 c++ binder 的 BpBinder 一一对应.

binder_ref 的主要成员有:

  • binder_proc * proc

    binder_ref 所在的进程 (使用这个 client 的进程)

  • binder_node * node

    这个 binder_ref 所指向的 binder_node (client 对应的 service)

  • uint32_t desc
  • rb_node_desc/rb_node_node

    与 binder_proc 的 refs_by_desc/refs_by_node 配合, 以便 binder_proc 可以根据 desc/node 很快的找到 desc/node 对应的 binder_ref

    desc 即 "descriptor"

    binder_ref 与 binder_node 实际上指的一个东西,即所谓的"一体两面", binder_node 是从 server 的角度来看, 而 binder_ref 是从 client 的角度来看.

    binder_ref 代表一个 client 端的 proxy, binder_node 类似于 server 端的 stub.

    binder_node 与 binder_ref 是`一对多`的关系, 一个进程的某一个 binder_node 可能有多个进程的多个 binder_ref 引用它, 即多个进程的 client 使用同一个进程的同一个 service.

1.4. binder_proc, binder_node, binder_ref 的关系:

          +--------------------+           +--------------------+                  +--------------------+
+-------->+ server binder_proc |           |client1 binder_proc |                  |client2 binder_proc |
|         +--------------------+           +--------------------+                  +--------------------+
|         |    rb_root nodes   |           |   refs_by_desc     +---+              |   refs_by_desc     +---+
|         +---------+----------+           +--------------------+   |              +--------------------+   |
|                   |                      |   refs_by_node     +-+ |              |   refs_by_node     +-+ |
|  +----------------+                      +--------------------+ | |              +--------------------+ | |
|  |                                                              | |                                     | |
|  |                                                              | |                                     | |
|  |    +-----------+                       +----------------+    | |               +----------------+    | |
|  |    |binder_node|<-----+-----+          |  binder_ref    |    | |               |  binder_ref    |    | |
|  |    +-----------+      |     |          +----------------+    | |               +----------------+    | |
|  +--->+  rb_node  |      |     |          |   desc         |    | |               |   desc         |    | |
|       +-----------+      |     |          +----------------+    | |               +----------------+    | |
+-------+   proc    |      |     |          |  rb_node_desc  |<---+-+               |  rb_node_desc  |<---+-+
        +-----------+      |     |          +----------------+    |                 +----------------+    |
                           |     |          |  rb_node_node  |<---+                 |  rb_node_node  |<---+
                           |     |          +----------------+                      +----------------+
                           |     +----------+  node          |      +---------------+  node          |
                           |                +----------------+      |               +----------------+
                           +----------------------------------------+

1.5. binder_node 和 binder_ref 的初始化

binder_node 的初始化是通过 binder_new_node

binder_ref 的初始化是通过 binder_get_ref_for_node

binder_driver 的 binder_transaction 函数在处理 flat_binder_object 时会负责 binder_node 和 binder_ref 的初始化

binder_transaction()
  // 处理 flat_binder_object
  switch (fp->type) {
    case BINDER_TYPE_BINDER:
    case BINDER_TYPE_WEAK_BINDER:
      struct binder_node *node = binder_get_node(proc, fp->binder);
      // 当前 proc 打算通过 parcel 将一个 binder stub 传递给另一个进程
      if (node == NULL):
        // 在当前 proc 中创建 binder_node
        node = binder_new_node(proc, fp->binder, fp->cookie);

      // 在 target_proc 中查看是否已经存在一个对应的 binder_ref,
      // 若不存在, 则在 target_proc 中新建一个 binder_ref
      ref = binder_get_ref_for_node(target_proc, node);
      fp->type = BINDER_TYPE_HANDLE;
      // 修改原 flat_binder_object 的 handle 为 target_proc 中对应的 binder_ref 的 desc
      fp->handle = ref->desc;

    case BINDER_TYPE_HANDLE:
    case BINDER_TYPE_WEAK_HANDLE:
      struct binder_ref *ref = binder_get_ref(proc, fp->handle);
      // proc 要传递 ref, 则 ref 必定存在于 proc 中
      assert (ref != NULL);
      if (ref->node->proc == target_proc):
        // ref->node->proc == target_proc, 表示要传递的 ref 的 node 已经位于 target_proc,
        // 则 target_proc 已经可以直接访问 ref 对应的 node 了, 下面只需要将 flat_binder_object
        // 中的 ref 直接修改为相应的 node.
        fp->type = BINDER_TYPE_BINDER;
        fp->binder = ref->node->ptr;
        fp->cookie = ref->node->cookie;
      else:
        // ref->node 不存在 target_proc, target_proc 需要通过一个新的 binder_ref 引用这个 node
        struct binder_ref *new_ref;
        new_ref = binder_get_ref_for_node(target_proc, ref->node);
        fp->handle = new_ref->desc;

binder_node 和 binder_ref 在 parcel 中传递导致 target_proc 中相应的 bindrr_node 和 binder_ref 被初始化, 例如:

  1. service_manager 的 addService 方法, 要 add 的 service 是 BINDER_TYPE_BINDER 类型 (BBinder), 导致调用者自己的 proc 中生成对应的 binder_node, 同时 service_manager 的 proc 生成对应的 binder_ref
  2. service_maanger 的 getService 方法, 需要返回一个 binder, driver 在处理类型为 BINDER_TYPE_HANDLE 的 flat_binder_object 时会在调用者的 proc 中生成对应的 binder_ref (假设该 binder_node->proc != target_proc)
  3. bindService, 与 getService 类似.

1.5.1. 总结

node, ref 的初始化发生在 flat_binder_object 转换期间, 是 "按需" 初始化的.

1.6. binder_node 和 binder_ref 的删除/释放

ref 和 node 的删除的过程类似于基于引用计数的 GC 过程, 主要是通过 binder_dec_ref 和 binder_dec_node.

具体参考 binder 引用计数.

1.7. binder driver command protocol

binder driver 与上层 IPCThreadState 通过一系统的 binder driver command 来通讯, 上层与 driver 通过 IPCTheadState 中的 mIn 和 mOut 两个 parcel 进行数据交换, command 会先被写入这两个 parcel, 然后根据 command 的不同,再写入其它数据 (例如, 若 command 为 BC_TRANSACTION, 则后续会写入一个 binder_transaction_data)

常用的 command 包括:

  • BC_XXX
    • BC_TRANSACTION
    • BC_REPLY
    • BC_FREE_BUFFER
    • BC_CLEAR_DEATH_NOTIFICATION
  • BR_XXX
    • BR_TRANSACTION
    • BR_REPLY
    • BR_TRANSACTION_COMPLETE
    • BR_DEAD_BINDER

所有的 BC_XXX 命令都是 user -> kernel, 所有的 BR_XXX 命令都是 kernel -> user

1.8. binder_thread

binder_thread 有两个含义:

  1. kernel 中的 binder_thread 数据结构
  2. IPCTheadState 发起的 binder thread #xxx 线程 (looper binder_thread)

kernel 每次处理 binder_transaction 时都需要在 kernel 中有一个和当前 thread 对应的 binder_thread.

若当前进程对应的是 client, 则 client 线程对应的 binder_thread 并不会在 proc-> wait 上等待, 它们是 transaction 的发起方, 只会在 binder_thread_write 完之后在 thread->todo 上取任务, 后者对应之前 transaction 的返回结果.

若当前进程是上层 IPCTheadState 注册的 binder thread #xxx, 它们是 transaction 的接收方, 则它们最终会通过 binder_thread_read 阻塞在 proc->wait 上, 接受 server binder_proc 的调度 (等待 client 唤醒), 并且应用层的 joinThreadPool 会保证这个 looper binder_thread 响应一次 transaction 后会再次 loop, 在 proc->wait 继续阻塞.

1.8.1. looper binder_thread 初始化

looper binder_thread 都是由应用层发起并且阻塞在 proc->wait 上的. 具体的, 都是通过 IPCTheadState.joinThreadPool.

每个需要 binder 通讯的进程(无论 server 或 client), 都需要至少有一个 looper binder_thread:

  • server 需要 looper, 因为 looper 需要被唤醒以处理 transaction
  • client 也需要 looper, 因为 client 也存在需要主动被唤醒的情况, 比如 death notification

native 程序都需要主动调用 IPCTheadState.joinThreadPool java 程序不需要, 因为 zygote 已经做了这一步.

除了应用主动启动 looper binder_thread, binder driver 还可以通过 BR_SPAWN_LOOPER 要求应用启动一个 looper.

1.8.1.1. joinThreadPool
1.8.1.2. BR_SPAWN_LOOPER

proc 有几个和 looper binder_thread 有关的统计数据:

  1. max_threads

    应用层通过 ioctl(BINDER_SET_MAX_THREADS) 设置的最大的 looper 数量, 默认为 15

  2. ready_threads

    阻塞在 proc->wait 上的 looper 个数, 这些 looper 等待被 transaction 唤醒, 所以为 "ready" 状态

    binder_thread_read:
      wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);
      if (wait_for_proc_work):
        proc->ready_threads++;
    
      // wait_event_interruptible
      // ...
      // looper 被唤醒
      if (wait_for_proc_work):
        proc->ready_threads--;
    
  3. requested_threads & requested_threads_started

    requested_threads 指 driver 已经发出 BR_SPAWN_LOOPER, 但还没有真正启动的 looper 个数, 即"等待启动" 的状态

    requested_threads_started 指上层通过 BC_REGISTER_LOOPER 启动的 looper 个数, 因为每个进程的都存在一个通过 BC_ENTER_LOOPER 启动的 looper, 所以当前进程已经启动的 looper 个数实际上 requested_threads_started + 1

    binder_thread_read:
      // ...
      // 从 proc->wait 唤醒并装备好 binder_transaction_data
      if spawn looper is needed:
        proc->requested_threads++;
        put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer)
    
    binder_thread_write:
      case BC_REGISTER_LOOPER:
        // BC_REGISTER_LOOPER 一定是应用层为了响应 driver 的 BR_SPAWN_LOOPER 请求导致的
        assert (proc->requested_threads != 0)
        // requested_threads 与 requested_threads_started 是此消彼长的关系           
        proc->requested_threads--;
        proc->requested_threads_started++;
    
1.8.1.3. 何时需要 spawn looper
proc->requested_threads + proc->ready_threads == 0 && proc->requested_threads_started < proc->max_threads

requested_threads + ready_threads == 0, 实际就是 requested_threads == 0 && ready_threads == 0, 即
1. requested_threads 为 0 , 表示不存在 "正在启动" 的 looper
2. ready_threads, 表示已经启动的 looper 中没有 ready 的, 都在忙着处理别的 transaction.
这种情况下如果再来一个 transaction 必然会在 proc->todo 上等待.

proc->requested_threads_started < proc->max_threads, 表示当前启动 looper 不超过 max_threads

1.9. binder_buffer

  • binder_buffer is used during ONE binder transaction to save request(in the target_proc's) and reply data (in the host_proc's) and this buffer is mmap to user-mode directly. so that user-mode BBinder can access binder_buffer directly.
  • every binder_proc has it's own buffer, size limited to 1M-8k, driver will allocate one binder_buffer from the buffer for every transaction.
  • one binder_proc's bind_buffers are organized in rb_tree, every node control a sized buffer.
  • the rb_tree use `best-fit` rule to allocate binder_buffer, and can `merge/split` on demand to reduce external memory fragmentation.

all buffers are in a continuous memory block:

0                       1K      	       	       	       	       	 1M-8K
+----------------------------------------------------------------+-------+
|   data1: 1K,allocated |   data2: 2k, free |   data3 521k, alloc| ....  |
+----------------------------------------------------------------+-------+

binder_buffer(s) are organized by buffers/free_buffers/allocated_buffers in rb_tree

                          +-----------------+
                          |     binder_proc |
                          +-----------------+
+-------------------------+     buffers     |
|                         +-----------------+
|  +----------------------+ free_buffers    |
|  |                      +-----------------+
|  |                      |allocated_buffers+------------------------+
|  |                      +-----------------+                        |
|  |                                                                 |
|  |                                                                 |
|  |   +---------------------+             +---------------------+   |
|  |   |  binder_buffer      |             |  binder_buffer      |   |
|  |   +---------------------+             +---------------------+   |
|  +-->+rb_node(free or not) |             |rb_node(free or not) |<--+
|      +---------------------+             +---------------------+
+----->+   list_head entry   +------------>|   list_head entry   |
       +---------------------+             +---------------------+
       |   data_size         |             |   data_size         |
       +---------------------+             +---------------------+
       |   data[0]           |             |   data[0]           |
       +---------------------+             +---------------------+

client data are mmap to SERVER's binder_buffer.

 User mode   +-----------------+                                       +-----------------+
             |   process A     |                                       |   process B     |
             +-----------------+            copy_from_user()           +-----------------+
             |   Parcel data   +------------------------+              |                 |
             +-----------------+                        |              +-------------^---+
                                                        |                            |
--------------------------------------------------------+----------------------+---------------
                   binder driver                        |                      |
 Kernel mode       +------------------------------------+----------------------+-------------+
                   |                                    V                      |             |
                   |     +----------------+          +--+-------------+        |             |
                   |     | binder_proc B  |   +----->+ binder_buffer  +--------+             |
                   |     +----------------+   |      +----------------+   mmap to B process  |
                   |     |  allocated_buf +---+      |  parcel data   |                      |
                   |     |                |          |  from A        |                      |
                   |     |                |          +----------------+                      |
                   |     +----------------+                                                  |
                   +-------------------------------------------------------------------------+

1.9.1. binder_buffer 的初始化

上层是通过 mmap 访问 binder_buffer 的.

ProcessState.cpp
======
ProcessState::ProcessState()
  // #define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2))
  mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
1.9.1.1. 1M-8k

BINDER_VM_SIZE 大小为 1M-8k, 为什么要减 8K?

因为 binder_buffer 在内核空间中通过 vmalloc 分配, kernel 在每个 vmalloc 区域后会插入一个 guard page (4K), 但我看不出来为什么要减 8K 而不是 4K …

Modify the binder to request 1M - 2 pages instead of 1M. The backing store in the kernel requires a guard page, so 1M allocations fragment memory very badly. Subtracting a couple of pages so that they fit in a power of two allows the kernel to make more efficient use of its virtual address space.

1.9.2. binder_buffer 的释放

binder_buffer 的释放是通过 Parcel::freeData, 具体参考 Parcel::freeData

1.9.3. 页表

  1. 上层建立 vma 后, driver 需要分配物理页. binder driver 会在 binder_alloc_buf 时直接分配物理页, 而不采用 nopage 的方式.
  2. 为了便于 driver 访问 binder_buffer, binder_buffer 会同时存在于进程页表与内核页表中, 并且 driver 会保证两个页表指向相同的物理页 (如果不使用内核页表, driver 代表 client 访问 server 的 binder_buffer 会比较麻烦)
binder_mmap
======
binder_mmap:
  // vma 对应 kernel 已经分配好的进程页表
  // area 对应 driver 分配的内核页表
  // get_vm_area 相当于不分配物理页 vmalloc, 所以这个 area 的线性地址范围大约是 3G+896M ~ 4G
  area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
  // proc->buffer 是 binder_buffer 在 kernel 中的线性地址   
  proc->buffer = area->addr;
  // user_buffer_offset 用来做两个地址的转换
  proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;

binder_alloc_buf
======
binder_alloc_buf:
  // 先从 free_buffers 中分配一个 binder_buffer
  // 然后通过 binder_update_page_range 分配物理内存并修改页表
  binder_update_page_range(proc, buffer->data /* start */, end_page_addr /* end */)
    vma = proc->vma;
    // 这里的 start 是内核地址空间中 binder_buffer 的首地址
    for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE):
      // 直接分配一个物理页, 不再信赖 page fault.
      *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
      // 在 kernel 页表插入一项且其物理页为 page
      tmp_area.addr = page_addr;
      tmp_area.size = PAGE_SIZE + PAGE_SIZE /* guard page? */;
      page_array_ptr = page;
      map_vm_area(&tmp_area, PAGE_KERNEL, &page_array_ptr);
      // 修改 target_proc 的进程页表,将物理页也指向 page
      user_page_addr = (uintptr_t)page_addr + proc->user_buffer_offset;
      vm_insert_page(vma, user_page_addr, page[0]);


client tr -> server binder_buffer
======
binder_transaction:
  copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)


server binder_buffer -> server tr
======
binder_thread_read:
  tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset;

内核地址空间中 binder_buffer 的地址:

[    3.399371] sunway: proc->buffer: f3800000
[    3.409164] sunway: proc->buffer: f2fc0000
[    3.771656] sunway: proc->buffer: f3900000
[    3.873044] sunway: proc->buffer: f3a00000
[    3.898036] sunway: proc->buffer: f3b00000
[    4.002905] sunway: proc->buffer: f3c00000
[    4.059095] sunway: proc->buffer: f3d00000
[    4.076636] sunway: proc->buffer: f3e00000
[    4.346371] sunway: proc->buffer: f4700000
[    8.574031] sunway: proc->buffer: f4500000
[   11.818651] sunway: proc->buffer: f4600000
[   11.837406] sunway: proc->buffer: f4d00000
[   11.852474] sunway: proc->buffer: f5500000
[   11.877071] sunway: proc->buffer: f5600000
[   12.689051] sunway: proc->buffer: f2d00000
[   13.131303] sunway: proc->buffer: f3700000
[   13.196740] sunway: proc->buffer: f3f00000
[   13.220509] sunway: proc->buffer: f4b00000
[   13.241774] sunway: proc->buffer: f4f00000
[   13.251766] sunway: proc->buffer: f5700000
[   13.467537] sunway: proc->buffer: f5b00000
[   13.679431] sunway: proc->buffer: f5f00000
[   13.731342] sunway: proc->buffer: f6500000
[   13.928822] sunway: proc->buffer: f6600000
[   14.070935] sunway: proc->buffer: f6700000
[   14.773066] sunway: proc->buffer: f0a00000
[   14.864689] sunway: proc->buffer: f0b00000
[   15.016303] sunway: proc->buffer: f0c00000
[   15.104307] sunway: proc->buffer: f0d00000
[   15.305597] sunway: proc->buffer: f0e00000
[   15.370854] sunway: proc->buffer: f0f00000
[   15.513454] sunway: proc->buffer: f2100000
[   15.681259] sunway: proc->buffer: f2200000
[   15.752318] sunway: proc->buffer: f2300000
[   73.915009] sunway: proc->buffer: f2400000

这些地址都为 3.8G 以上 (get_vm_area) 并且有许多 (N5上有 35个), 如果当前有 100 多个 java 进程, 或者将 binder_buffer 增大到 5M 会如何 …

binder_buffer使用内核页表(get_vm_area)的实现方式决定了:

  1. binder_buffer 不能太大
  2. java 进程不能太多.

另外, binder_buffer 的大小也限制了 binder_thread 的数量.

1.9.4. best-fit

1.10. binder_transaction_data

binder_transaction_data 是 kernel 和 user space 都需要使用的数据结构, 它是两者交换数据的桥梁

binder_transaction_data stores request/reply data, and in most time, is bitwise copied to binder_buffer, but there are several exceptions:

  • Binder

    The BBinder wrote in binder_transaction_data is transformed to BpBinder and vice versa

  • file descriptor

    new file descriptor is created in the target process, and old file descriptor is transformed to the newly created one.


*binder_transaction_data*

 +-------------+------+-----------+------+----------+-------------+---------+-----------+-----------+-----------+
 |   target    |      |   code    |      |          |             |         |           |           |           |
 | (handle/ptr)|cookie| (command) | flags|sender_pid| sender_euid |data_size|offset_size| buffer_ptr|offset_ptr |
 +-------------+------+-----------+------+----------+-------------+---------+-----------+-----------+-----------+
                                                                                              |           |
  +-------------------------------------------------------------------------------------------+    +------+
  | -+---------+----+--------------+--------+-----+-----+------+-----+----------                   v
  |  | type    |flag| binder/handle| cookie | ....|type | flag | ... |                             +-----+--+-------+--------------
  >--+(binder, |    |              |        |     |     |      |     | normal data (int,...)       | offset1|offset2| ...
     | handler,|    |              |        |     |     |      |     |                             +---+----+---+---+--------------
     | fd..)   |    |              |        |     |     |      |     |                                 |        |
     ^---------+----+--------------+--------+-----^-----+------+-----+---------                        |        |
     |    *flat_binder_object*                    |                                                    |        |
     +--------------------------------------------+----------------------------------------------------+        |
                                                  +-------------------------------------------------------------+


1.11. flat_binder_object

parcel 内部使用 flat_binder_object 来表示 fd, binder, handle 这些特殊的 "object".

正常情况下, parcel 的数据都是平铺在 parcel->mData 中, 例如 int, float, string 等. 但 fd, binder, handle 这些特殊的 object 都是以 flat_binder_object 的形式平铺在 mData 中的. binder driver 需要使用这些信息对它们进行特殊处理.

另外, parcel 中的 mObjects (即 binder_transaction_data 中的 offsets) 是针对 flat_binder_object 的一个索引, 这样 binder driver 在处理 binder_transaction_data 时可以"快速"的找到所有的 flat_binder_object 以便进行特殊处理 (参考 binder_transaction 函数)

1.12. binder_transaction & binder_work

binder_transaction 和 binder_work 是 binder_thread 之间沟通的桥梁.

  1. client binder thread 要唤醒 proc->wait 上的 looper binder thread 前,需要将数据 (binder_transaction_data) 封装在 binder_transaction 中, 并把这个 binder_transaction push 到它的 binder_transaction_stack, 然后通过将 transaction 对应 binder_work 加到 proc->todo 的形式通知 looper, 后续 looper 被唤醒后可以从 proc->todo 中获得 binder_transaction
  2. looper 被唤醒后会从 binder_transaction 中获得 binder_transaction_data,并将这个 binder_transaction push 到它自己的 binder_transaction_stack.
  3. looper 需要唤醒之前的 client binder thread 之前, 需要根据它自己的 binder_transaction_stack 找到 client binder thread, 然后再构造新的 binder_transaction (包括需要返回的 binder_transaction_data) 和 binder_work, 然后把 binder_work 加到 client_thread->todo 中, 后续 client_thread 被唤醒后可以在 thread->todo 中获得 binder_transaction

1.13. binder 引用计数

binder 通过引用计数来控制 binder_ref/binder_node 的释放.

binder 引用计数涉及到两个部分内容:

  • C++ 层面的 BpBinder / RefBase / StrongPointer(sp)
  • driver 层面 binder_ref/binder_node 的引用计数

binder_node, binder_ref, BpBinder(RefBase, sp) 三者之间的对应关系:

  1. BpBinder 本身是 RefBase, 应用层的多个 BpBinder 的 StrongPointer (sp) 可以指向同一个 BpBinder. 通过 RefBase 自身的引用计数, 应用层的一些工作不必进入到 driver.
  2. 同一个进程的多个 RefBase (BpBinder) 对应一个 binder_ref
  3. 不同进程的多个 binder_ref 对应一个 binder_node

以上三种多对一的关系导致了三级的引用计数

  1. RefBase
    • incStrong
    • decStrong
  2. binder_ref
    1. BC_ACQUIRE
      • binder_inc_ref
    2. BC_RELEASE
      • binder_dec_ref
  3. binder_node
    • binder_inc_node
    • binder_dec_node

dec_level_n 大致是:

dec_level_n:
  local_count --              // level_n_1
  if (local_count == 0):      // level_n_2
    release_resource          // level_n_3
    dec_level_(n++)           // level_n_4

inc_level_n 大致是:

inc_level_n:
  if (local_count == 0):
    inc_level_(n++)
  local_count ++

1.13.1. reference 相关场景

1.13.1.1. 引用计数的 release/dec
1.13.1.1.1. Java 层 BinderProxy 被 finalize
RefBase: level_1
==================
Binder.finalize()
  android_os_BinderProxy_destroy(JNIEnv* env, jobject obj)
    BpBinder.decStrong()
      c = android_atomic_dec(&refs->mStrong)               // level_1_1
      if (c == 1):                                         // level_1_2
        const_cast<RefBase*>(this)->onLastStrongRef(id)    
          IPCThreadState::decStrongHandle()
            mOut.writeInt32(BC_RELEASE);                   // level_1_4

binder_ref: level_2
==================
binder_thread_write
  case BC_RELEASE:
    binder_dec_ref(ref, 1);
      ref->strong--;                                       // level_2_1     
      if (ref->strong == 0):                               // level_2_2
        binder_delete_ref()                                // level_2_3
        binder_dec_node(ref->node, strong, 1);             // level_2_4


binder_node: level_3
==================
binder_dec_node
  node->internal_strong_refs--;                            // level_3_1
  if node->internal_strong_refs == 0:                      // level_3_2
    rb_erase(&node->rb_node, &node->proc->nodes);          // level_3_3
1.13.1.2. 引用计数的 acquire/inc
1.13.1.2.1. binder_ref 的 acquire

存在两种情况:

  1. binder_transaction 对 flat_binder_object 处理时会调用 binder_inc_ref 增加 binder_ref 的引用计数
  2. 因为 service_manger 对应的 handle 固定为 0, 所以应用一般不需要在 parcel 中传递它, 所以上层要获得 service_manger 的 BpBinder 时需要显式 acquire, 例如 ProcessState::getContextObject

    ProcessState::getContextObject
      getStrongProxyForHandle(0)
        b = new BpBinder(0);
        sp<IBinder> result = b;
        // sp 为 StrongPointer 的意思, 它的一个赋值构造函数为:
          sp<T>::sp(T* other):
            other->incStrong(this);                  
              RefBase::onFirstRef();
                IPCThreadState::incStrongHandle(0);
                  mOut.writeInt32(BC_ACQUIRE);
        return result;
    

1.14. binder death

一般情况下, 进程退出(正常或异常)时 kernel 会负责关闭所有的 fd, 当 /dev/binder 对应的 fd 被关闭时, 会触发 binder 的 death 流程.

close(binder_fd)
  driver::binder_release()
    driver::binder_deferred_release()

1.14.1. DeathRecipient

1.14.1.1. binder death 如何触发 DeathRecipient
driver::binder_deferred_release()
  foreach node->refs:
    if (ref->death):
      ref->death->work.type = BINDER_WORK_DEAD_BINDER;
      list_add_tail(&ref->death->work.entry, &ref->proc->todo);
      wake_up_interruptible(&ref->proc->wait);

当 proxy 端的 binder_thread 被唤醒后, 它会读到一个 BR_DEAD_BINDER 命令.

binder_thread_read:
  case BINDER_WORK_DEAD_BINDER:
    death = container_of(w, struct binder_ref_death, work);
    cmd = BR_DEAD_BINDER;
    put_user(cmd, (uint32_t __user *)ptr)
    put_user(death->cookie, (void * __user *)ptr)

  IPCThreadState.waitForResponse
    // cmd = BR_DEAD_BINDER        
    cmd = (uint32_t)mIn.readInt32();
    IPCThreadState::executeCommand(cmd)
      case BR_DEAD_BINDER:
        // 读到 death->cookie, 即 BpBinder 的地址
        BpBinder *proxy = (BpBinder*)mIn.readInt32();
        proxy->sendObituary();
          foreach ob in mObituaries:
            ob.recipient.binderDead()
1.14.1.2. 注册 DeathRecipient
BpBinder.linkToDeath
  Obituary ob;
  ob.recipient = recipient;
  IPCTheadState::requestDeathNotification(mHandle, BpBinder);
    mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION);
    mOut.writeInt32((int32_t)handle);
    // BpBinder's 的地址被保存到 ref->deaht->cookie
    mOut.writeInt32((int32_t)BpBinder); 
      driver::BC_REQUEST_DEATH_NOTIFICATION
        ref->death->cookie=*cookie*; ;;BpBinder's local address
  mObituaries->add(ob);

1.14.2. send failed reply

client 正在等待 server 唤醒它, 但 server 挂掉了, client 怎么办? server 会 send failed reply.

server:
======
binder_deferred_release:
  for each binder_thread (thread) in proc->threads:
    binder_free_thread(proc, thread);
      t = thread->transaction_stack;
      if (t && t->to_thread == thread):
        // t->to_thread == thread, 表示:
        // 1. looper thread 正在处理一个 transaction t
        // 2. t 是一个与 looper 相关的 client thread, 这个 thread
        // 要被 send_failed_reply, 因为后面的 binder_release_work 并不
        // 会处理这个 client thread
        send_reply = t;
      if (send_reply):
        binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
          target_thread = t->from;
          target_thread->return_error = BR_DEAD_REPLY;
          wake_up_interruptible(&target_thread->wait);
          // GOTO !HERE!

  binder_release_work(&proc->todo);          
    for each BINDER_WORK_TRANSACTION (w) in list:
      t = container_of(w, struct binder_transaction, work);
      binder_send_failed_reply(t, BR_DEAD_REPLY);
        // GOTO !HERE!

client:
======
android_os_BinderProxy_transact:
  status_t err = target->transact(code, *data, reply, flags);
    return IPCThreadState::self()->transact()
      return waitForResponse(reply);
        talkWithDriver()
          binder_thread_read:
            // !HERE!
            if no work in thread->todo:
              goto retry
            retry:
              if (thread->return_error != BR_OK && ptr < end):
                put_user(thread->return_error, (uint32_t __user *)ptr)
                return -EFAULT;
        // 这里 cmd = BR_DEAD_REPLY
        cmd = (uint32_t)mIn.readInt32();
        switch cmd:
        case BR_DEAD_REPLY:
          err = DEAD_OBJECT;
          goto finish;
  // target->transact 返回 err 为 BR_DEAD_REPLY, 根据 err 抛 java exception           
  if (err != NO_ERROR && err != UNKNOWN_TRANSACTION):
    // err = DEAD_OBJECT
    signalExceptionForError(err);
      switch (err):
      case DEAD_OBJECT:
        jniThrowException(env, "android/os/DeadObjectException")
      default:
        // 这里可能是 RemoteException 的主要来源?
        jniThrowException(env, canThrowRemoteException
          ? "android/os/RemoteException" : "java/lang/RuntimeException", msg.string());

1.15. binder stats

/sys/kernel/debug/binder/

1.16. TF_ONE_WAY

1.16.1. oneway 关键字

TF_ONE_WAY 是上层 transact 函数的参数, 表示不需要等待对方返回. 在 aidl 中可以用 oneway 关键字:

test.aidl
oneway int test();

生成的 java 代码为:

public int x() throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    int _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        mRemote.transact(Stub.TRANSACTION_x, _data, null, android.os.IBinder.FLAG_ONEWAY);
    }
    finally {
        _data.recycle();
    }
    return _result;
}

可见, 使用了 oneway 后对方的返回值是忽略的, 因为 transact 时传入的 reply 为 null, 是无法拿到返回值的.

1.16.2. TF_ONE_WAY 的实现

TF_ONE_WAY 表示 transaction 是更早的返回而不等待, 具体在哪里返回?

client 端的 binder_transaction():
  // 在唤醒 proc->wait 之前
  tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
  // 放了一个类型为 BINDER_WORK_TRANSACTION_COMPLETE 的 binder_work 在自身 thread->todo
  list_add(&tcomplete->entry, &thread->todo);
  // 唤醒 proc->wait 并返回, 然后 client 端会接着调用 binder_thread_read

client 端的 binder_thread_read():
  // 这里为 false, 因为两个条件都不满足... thread->todo 上有上一步放上的 tcomplete
  wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);
  if (wait_for_proc_work):
    // 这里不会走...(looper 才会走这一步)
    wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));

  // 因为 thread->todo 上有一个 tcomplete, 所以 binder_has_thread_work 为真,
  // wait_event_freezable 会直接返回而不会等待.
  wait_event_freezable(thread->wait, binder_has_thread_work(thread));
  while (1):
    // 从 thread->todo 中拿到 tcomplete 这个 work, 第一次进入 while 时
    // 这里成立
    if (!list_empty(&thread->todo)):
      w = list_first_entry(&thread->todo, struct binder_work, entry);
    else if (!list_empty(&proc->todo) && wait_for_proc_work)        
      w = list_first_entry(&proc->todo, struct binder_work, entry);
    else:
      // 再次进行 while 时这里会成立 ...
      break;

    switch (w->type):
      case BINDER_WORK_TRANSACTION_COMPLETE:
        cmd = BR_TRANSACTION_COMPLETE;
        put_user(cmd, (uint32_t __user *)ptr)
    // w 不是不一个 BINDER_WORK_TRANSACTION, 所以这里条件成立, 回到 while 开头 ...    
    if (!t)
      continue;        
  // while 结束, binder_thread_read 返回  

client 端的 IPCThreadState.transact
  IPCThreadState.transact()
    if ((flags & TF_ONE_WAY) != 0):
      waitForResponse(NULL, NULL);
        while (1):
          talkWithDriver()
          cmd = (uint32_t)mIn.readInt32();
          switch (cmd):
            case BR_TRANSACTION_COMPLETE:
              // 这里 reply, acquireResult 为 null, 不会等待 BR_REPLY, 而是直接跳出循环返回   
              if (!reply && !acquireResult) goto finish;

所有的 client transaction 在 binder_thread_read 真正阻塞在 thread->wait 之前, 都会先返回 BR_TRANSACTION_COMPLETE 给上层, 上层的 while 循环会再次 talkWithDriver 以便第二次真正在 thread->wait 上阻塞.

不论是否为 TF_ONE_WAY, BINDER_WORK_TRANSACTION_COMPLETE 的流程都会走一遍, 不同的是, TF_ONE_WAY 会阻止第二次的 talkWithDriver, 从而避免阻塞.

另外, 除了 TF_ONE_WAY, target proc 的 sendReply 也是使用相同的方法 (waitForResponse(NULL, NULL)), 因为 sendReply 后 target proc 不需要再阻塞的…因为没人会唤醒它.

1.16.3. async_transaction

正常情况下, binder_transaction () 时会将 binder_work 挂在 proc->todo 上, 并立即通过 proc->wait 唤醒一个 looper 来处理这个 binder_work.

但若当前 transaction 有 TF_ONE_WAY 标记, 则这个 binder_work 会被挂在 target_node->async_todo 上, 并且不唤醒任何 looper.

target_node->async_todo 上的 binder_work 何时被处理?

binder_thread_write
  // 所有 looper 在处理 transaction 时都必定会调用 BC_FREE_BUFFER
  case BC_FREE_BUFFER:
    if (buffer->async_transaction && buffer->target_node):
      // 从 target_node->async_todo 上取一个任务加到 thread->todo
      // 后续的 binder_thread_read 会取到这个任务并执行
      list_move_tail(buffer->target_node->async_todo.next, &thread->todo);

可见, async_transaction 是通过 BC_FREE_BUFFER 触发的, 参考后面对data parcel 的 freeData 的说明, async_transaction 最终还是会由looper binder thread 去处理.

1.16.4. 总结

收到 BR_TRANSACTION_COMPLETE 表示 target proc 的 looper 已经被唤醒,上层可以决定是否需要再次 talkWithDriver 以等待 target proc 的 looper 返回.

1.17. binder 优先级相关

binder driver 工作时会考虑 client 进程的优先级, 主要有两方面:

  1. binder_lock

    binder driver 中的大部分函数都需要通过 binder_lock/binder_unlock 来加以保护, 而 binder_lock 时会根据 client 进程的优先级决定谁先获得 lock

  2. binder_set_nice

    client 进程的优先级会通过 binder_transaction 传递给 server, server 会通过 binder_set_nice "继承" client 的优先级.

1.17.1. binder_lock

大部分 binder driver 的函数都需要用 binder_lock 来保护, 主要的是 binder_ioctl: binder_ioctl 一开始就会调用 binder_lock, 直到阻塞到 binder_thread_read 前才会释放 binder_lock. 即, server 应用层代码并不持有 binder_lock.

aosp 的 binder_lock 实现上使用了 rt_mutex_lock, 而 rt_mutex_lock 本身就会根据进程优先级决定谁先获得 lock

注: android 6.0 似乎把 rt_mutex_lock 又修改为 mutex_lock 了, (见 msm kernel 的这个提交: b378873b7f2c96141b72b59fc4ce73c1f60b98e2)

1.17.2. binder_set_nice

binder driver 通过 binder_transaction 中实现了优先级继承

  1. client 的优先级保存在 binder_transaction 中

    binder_transaction:
      // client 的优先级保存在 t->priority 中
      t->priority = task_nice(current);
      // 将 t 通知给某个 looper 并唤醒
      list_add_tail(&t->work.entry, proc->todo);
      wake_up_interruptible(target_wait);
    
  2. looper 拿到这个优先级并做相应设置

    binder_thread_read:
      // looper
      if (wait_for_proc_work):
        // looper 阻塞时使用其默认优先级
        binder_set_nice(proc->default_priority);
      // ...
      // looper 被唤醒
      // looper 保存旧的优先级
      t->saved_priority = task_nice(current);
      // 若 TF_ONE_WAY (async transaction), 则不修改优先级
      if  (! t->flags & TF_ONE_WAY):
        binder_set_nice(min(target_node->min_priority,t->priority));
      // 后续 server 被唤醒  
      cmd = BR_TRANSACTION;
    
  3. looper 返回数据给 client 并恢复其优先级

    binder_transaction:
      if (reply):
        binder_set_nice(in_reply_to->saved_priority);
    
  4. binder_node->min_priority

    binder_node->min_priority 表示 server 在执行这个 binder_node 时优先级至少为 min_priority,这个值是在 binder_node 初始化时估计 flat_binder_object 的 flag 决定的, 最终决定它的是 parcel.flatten_binder

    Parcle::flatten_binder:
      // 看起来目前这个 0x7f 是写死的...所以每个 binder_node
      // 的 min_priority 都会是 0x7f
      obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
      // ...
    
1.17.2.1. 综上
  • 通过 binder_transaction->priority, client 可以将它的优先级传递给 server
  • 通过 binder_transaction->saved_priority, server 可以恢复其优先级
  • 另外, parcel 在传递 binder_node 时可以配置一个 min_priority, 但目前看起来是一个固定值.

1.18. service_manager

refere to:

  1. binder_become_context_manager
  2. ProcessState::getContextObject

Author: [email protected]
Date: 2017-04-04 Tue 00:00
Last updated: 2019-11-06 Wed 20:20

知识共享许可协议