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
