Binder Driver
Table of Contents
- 1. Binder Driver
- 1.1. binder_proc
- 1.2. binder_node
- 1.3. binder_ref
- 1.4. binder_proc, binder_node, binder_ref 的关系:
- 1.5. binder_node 和 binder_ref 的初始化
- 1.6. binder_node 和 binder_ref 的删除/释放
- 1.7. binder driver command protocol
- 1.8. binder_thread
- 1.9. binder_buffer
- 1.10. binder_transaction_data
- 1.11. flat_binder_object
- 1.12. binder_transaction & binder_work
- 1.13. binder 引用计数
- 1.14. binder death
- 1.15. binder stats
- 1.16. TF_ONE_WAY
- 1.17. binder 优先级相关
- 1.18. service_manager
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 被初始化, 例如:
- service_manager 的 addService 方法, 要 add 的 service 是 BINDER_TYPE_BINDER 类型 (BBinder), 导致调用者自己的 proc 中生成对应的 binder_node, 同时 service_manager 的 proc 生成对应的 binder_ref
- service_maanger 的 getService 方法, 需要返回一个 binder, driver 在处理类型为 BINDER_TYPE_HANDLE 的 flat_binder_object 时会在调用者的 proc 中生成对应的 binder_ref (假设该 binder_node->proc != target_proc)
- 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 有两个含义:
- kernel 中的 binder_thread 数据结构
- 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 有关的统计数据:
max_threads
应用层通过 ioctl(BINDER_SET_MAX_THREADS) 设置的最大的 looper 数量, 默认为 15
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--;
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. 页表
- 上层建立 vma 后, driver 需要分配物理页. binder driver 会在 binder_alloc_buf 时直接分配物理页, 而不采用 nopage 的方式.
- 为了便于 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)的实现方式决定了:
- binder_buffer 不能太大
- 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 之间沟通的桥梁.
- 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
- looper 被唤醒后会从 binder_transaction 中获得 binder_transaction_data,并将这个 binder_transaction push 到它自己的 binder_transaction_stack.
- 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) 三者之间的对应关系:
- BpBinder 本身是 RefBase, 应用层的多个 BpBinder 的 StrongPointer (sp) 可以指向同一个 BpBinder. 通过 RefBase 自身的引用计数, 应用层的一些工作不必进入到 driver.
- 同一个进程的多个 RefBase (BpBinder) 对应一个 binder_ref
- 不同进程的多个 binder_ref 对应一个 binder_node
以上三种多对一的关系导致了三级的引用计数
- RefBase
- incStrong
- decStrong
- binder_ref
- BC_ACQUIRE
- binder_inc_ref
- BC_RELEASE
- binder_dec_ref
- BC_ACQUIRE
- 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
存在两种情况:
- binder_transaction 对 flat_binder_object 处理时会调用 binder_inc_ref 增加 binder_ref 的引用计数
因为 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 进程的优先级, 主要有两方面:
binder_lock
binder driver 中的大部分函数都需要通过 binder_lock/binder_unlock 来加以保护, 而 binder_lock 时会根据 client 进程的优先级决定谁先获得 lock
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 中实现了优先级继承
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);
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;
looper 返回数据给 client 并恢复其优先级
binder_transaction: if (reply): binder_set_nice(in_reply_to->saved_priority);
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:
- binder_become_context_manager
- ProcessState::getContextObject