Binder Overall
Table of Contents
- 1. Binder Overall
- 1.1. client 发起 transaction
- 1.2. binder driver 唤醒 server 端 binder_thread 并阻塞 client
- 1.3. server 之前需要启动 looper binder_thread 并阻塞
- 1.4. server 的 looper binder_thread 被唤醒
- 1.5. server 执行 transact
- 1.6. server send reply
- 1.7. binder driver 唤醒阻塞的 client
- 1.8. client 的 binder_thread 被唤醒
- 1.9. client 拿到返回结果
- 1.10. binder driver 之外的流程
- 1.11. 总结
1. Binder Overall
1.1. client 发起 transaction
1.1.1. IPCThreadState::transact
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
// - handle 对应 client 端 binder_ref 的 desc
// - code 为应用层的 transaction code
// - data 为 transact 的参数, 这个 parcel 的 mData 是 client 应用层通过 malloc 分配的, 和底层 binder buffer 无关
// - reply 用来容纳 transact 的返回值, 这个 parcel 现在还是 init 状态, 即它的 mData 实际为 null, 并未分配内存
// 后续 transact 返回时会通过 ipcSetDataReference 让 reply 的 mData "引用" 底层 binder buffer.
writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
waitForResponse(reply);
1.1.2. IPCTheadState::writeTransactionData
IPCThreadState::writeTransactionData(int32_t cmd,
uint32_t binderFlags,
int32_t handle,
uint32_t code, const Parcel& data, status_t* statusBuffer)
// - cmd 对应 binder driver command, 这里是 BC_TRANSACTION. BC_XXX 都是指 user -> kernel 的命令, 而 BR_XXX 是指从 kernel -> user 的命令
// - handle, 对应 binder_ref 的 desc
// - code, transction code
// - data, 应用层的参数
// 这个函数主要是将 data 中的数据写入一个 binder_transaction_data 中, binder_transaction_data 数据结构同时被 user space 和 kernel space 使用, 它
// 是两者交互的桥梁.
// binder_transaction_data 的结构大约是:
// 1. header, 包括 binder handle, transaction code, sender_pid, send_uid 等和 binder 相关的信息
// 2. 一个 buffer (data->ptr->buffer) 指针, 指向源 parcel (data) 的 mData buffer, 包含普通数据 (int, float, string ...) 和 flat_binder_object
// 3. 一个 offsets (data->ptr->offsets) 指针, 指向源 parcel 的 mObjects buffer, 是一个针对 flat_binder_object 的索引.
// 另外, 从 writeTransactionData 的实现也可以看到, 最终写到 mOut 中的是 binder_transaction_data, 而后者包含的只是源 parcel 中的 mData 和 mObjects 指针
// 最终对数据的复制发生在 kernel space.
// tr 的 head 部分
binder_transaction_data tr;
tr.target.handle = handle;
// tr.cookie 为 0, 这个值代表的是 server 端 BBinder 的地址, 而此时 client 并不知道是多少
// 后续 server 端从 binder_transaction 中重建 tr 时会给它正确的值
tr.cookie = 0;
tr.code = code;
tr.sender_pid = 0;
// tr 的 buffer, offsets 指针
tr.data_size = data.ipcDataSize();
tr.data.ptr.buffer = data.ipcData();
tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
tr.data.ptr.offsets = data.ipcObjects();
// 将 cmd (BC_TRANSACTION) 和 tr 写入 mOut
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
1.1.3. IPCTheadState::waitForResponse
IPCTheadState::waitForResponse(Parcel *reply) talkWithDriver() // ioctl 返回, 需要从 kernel 获得返回数据, 还是要通过 binder_transaction_data cmd = (uint32_t)mIn.readInt32();
1.1.4. IPCTheadState::talkWithDriver
IPCTheadState::talkWithDriver // talkWithDriver 的主要作用是: // 1. 将应用层提供的, 包含源 parcel 数据指针的 binder_transaction_data 通过 ioctl 交给 Kernel // 2. 等待 ioctl 返回, 此时 mIn 已经包含了 kernel 返回的 binder_transaction_data // 因为 talkWithDriver 时应用层需要将 mOut (包含要发送给 kernel 的 binder_transaction_data) // 以及 mIn (用来接收 kernel 返回的 binder_transaction_data) 都传递给 driver, // 所以封装了一个 binder_write_read 结构体做为 ioctl 参数 binder_write_read bwr; bwr.write_buffer = (uintptr_t)mOut.data(); bwr.read_buffer = (uintptr_t)mIn.data(); ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
1.1.5. 总结
- 将 parcel 中的 mData, mObjects 指针封装在 binder_transaction_data
- 将这个 binder_transaction_data 写入 mIn 这个 parcel 中
- 将 mIn 和 mOut 这两个 parcel 的 data 指针封装在 binder_write_read 中,通过 ioctl 以 BINDER_WRITE_READ 命令传递给 binder driver.
client 源 parcel 数据流向为:
client parcel -> binder_transaction_data -> IPCTheadState::mOut -> binder_write_read
1.2. binder driver 唤醒 server 端 binder_thread 并阻塞 client
binder_ioctl(filp, cmd, bwr) // 此时 cmd 为 BINDER_WRITE_READ copy_from_user(&bwr, ubuf, sizeof(bwr)) binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed); binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
1.2.1. binder_thread_write
binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed) // bwr.write_buffer 即 user space 的 mOut.data 指针, 更进一步, 这个 mOut.data 的数据是 cmd + binder_transaction_data // 读取开头的 cmd, 即 BC_TRANSACTION get_user(cmd, (uint32_t __user *)ptr) struct binder_transaction_data tr; // 读取紧接的 binder_transaction_data copy_from_user(&tr, ptr, sizeof(tr)) binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
1.2.2. binder_transaction
binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
// binder_transaction 需要根据 tr.target.handle 在当前 proc 中找到对应的 binder_ref,
// 然后找到 target proc
if (tr->target.handle):
// binder_get_ref 会使用 binder_proc->refs_by_desc 这颗 rb-tree 查找 handle 对应的 binder_ref
struct binder_ref *ref = binder_get_ref(proc, tr->target.handle);
target_node = ref->node;
// 找到 target_proc
target_proc = target_node->proc;
target_list = &target_proc->todo;
// 新的数据结构: binder_transaction
struct binder_transaction *t = kzalloc(sizeof(*t), GFP_KERNEL);
t->to_proc = target_proc;
t->code = tr->code;
// 从 target_proc 的 binder_buffer 上分配内存
t->buffer = binder_alloc_buf(target_proc, tr->data_size,tr->offsets_size)
// 把 binder_transaction_data 的 buffer 复制到 t->buffer->data, 这一步
// 的结果就是把 client 用户态的 parcel 的 mData 复制到 target proc 的 binder_buffer 中
copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)
// parcel 中的 mObjects 也被复制到 t->buffer
// 在原始的 parcel 中, mData 和 mObjects 是分别通过 malloc 分配的, 两个 buffer 并不连续,
// 但复制到 binder_buffer 后, 两者变成连续的了.
offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)
// 这时相当于已经拿到 client 的 源 parcel 的内容, 下面会解析 parcel 中的各 flat_binder_object, 以便对它们特殊处理.
// 所谓的特殊处理, 主要有两方面内容:
// 1. 针对 fd, 需要在 target 进程新建 fd (还记得 proc->files 么)
// 2. 针对 binder, 需要在 target_proc 初始化对应的 binder_node 和 binder_ref. 具体参考 "binder node/ref 的初始化"
for (; offp < off_end; offp++):
struct flat_binder_object *fp = (struct flat_binder_object *)(buffer->data + *offp);
switch (fp->type):
case BINDER_TYPE_BINDER:
// client 要传递 BBinder (binder node)
case BINDER_TYPE_HANDLE:
// client 要传递 BpBinder (binder ref)
// 若 binder_ref->node->proc == target_proc, 则将 flat_binder_object 中的原值替换为这个 binder_node
// 否则在 target proc 中新建一个指向 binder_ref->node 的新的 binder_ref, 并代替 flat_binder_object 中的 binder_ref
case BINDER_TYPE_FD:
// client 要传递 fd 到 server, 这时 driver 会在 target_proc 中创建新的 fd,
// 然后将 flat_binder_object 中的原始 fd 修改为新的 fd
// 当前 binder_thread (代表 client) 的 transaction_stack 栈顶设为 t
// 需要 transaction_stack 是因为后续这个 client thread 会阻塞, 需要 server 根据 transaction_stack 决定如果找到 client thread
// 并唤醒它以处理返回结果.
t->from_parent = thread->transaction_stack;
thread->transaction_stack = t;
// binder_transaction t 已经包含了足够的数据 (t->buffer 包含 parcel 的数据和修改过的 flat_binder_object)
// 将 t->work 加入到 target_list (target_proc->todo) 中
t->work.type = BINDER_WORK_TRANSACTION;
list_add_tail(&t->work.entry, target_list);
// 通过 target_proc->wait 唤醒 server 中在 target_proc->wait 上阻塞的某个 binder thread #xxx (looper thread), 以便去处理这个 binder_transaction
wake_up_interruptible(target_wait);
1.2.3. client 阻塞在 binder_thread_read
因为上一步设置了 transaction_stack , 所以这里的 client 的 binder_thread_read 会阻塞在 thread->wait 而不是 proc->wait, 后续 server 会唤醒这个 thread->wait (server 通过 binder_transaction 可以知道这个 transaction 来自哪个 binder_thread)
binder_thread_read: wait_event_freezable(thread->wait, binder_has_thread_work(thread));
1.3. server 之前需要启动 looper binder_thread 并阻塞
上面看以看到, client 需要唤醒在 target_proc->wait 上阻塞的某个 binder_thread, 这个 binder_thread 是通过 IPCTheadState::joinThreadPool 阻塞在 target_proc->wait 上的.
IPCThreadState::joinThreadPool
mOut.writeInt32(BC_ENTER_LOOPER)
// 这里是一个死循环, getAndExecuteCommand 返回后会再次 loop 导致又阻塞在 proc->wait 上
// 所以这类 binder_thread 称为 looper binder thread (与 client 那种只执行一次 binder_thread 区别开来)
do:
// now get the next command to be processed, waiting if necessary
getAndExecuteCommand();
talkWithDriver();
cmd = mIn.readInt32();
executeCommand(cmd);
1.3.1. 注册 looper: binder_thread_write
1.3.2. 阻塞在 binder_thread_read
binder_thread_read
// 若 thread->transaction_stack 不为 null, 表示该 thread 之前有未完成的 binder_transaction,
// 这时 thread 需要在 thread->wait 上 wait, 因为后续那个 binder_transaction 的 target_thread 会
// 唤醒 thread->wait.
// 若 thread->transaction_stack 为 null, 则该 thread 需要在 proc->wait 上 wait 以响应某个 binder_transaction
// 这里 thread->transaction_stack 为 null 会成立, 即 wait_for_proc_work 为真.
wait_for_proc_work = thread->transaction_stack == NULL && list_empty(&thread->todo);
if (wait_for_proc_work):
// ready_threads 和 binder thread spawn 有关
proc->ready_threads++;
if (wait_for_proc_work):
// 在 proc->wait 上阻塞
wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
else:
// 有未完成的 transaction, 在 thread->wait 上阻塞
wait_event_freezable(thread->wait, binder_has_thread_work(thread));
if (wait_for_proc_work):
proc->ready_threads--;
1.4. server 的 looper binder_thread 被唤醒
binder_thread_read
// 接上面的 wait_event_freezable
// 此时 binder_thread 已经通过 proc->wait 被唤醒
// 因为这个 binder_thread 是一个 transaction 的接受方, 所以它需要在 proc->todo 上取任务
// (若 binder_thread 是 transaction 的发起方, 则它需要从 thread->todo 上取任务, 表示之前发起的 transaction 返回)
struct binder_work *w = list_first_entry(&proc->todo, struct binder_work, entry);
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
// 此时 w->type 应该为 BINDER_WORK_TRANSACTION
t = container_of(w, struct binder_transaction, work);
...
}
// 找到了 client 传来的 binder_transaction
if (t->buffer->target_node) {
// 此处条件为真
// 从 binder_transaction (t) 构造 binder_transaction_data (tr)
struct binder_node *target_node = t->buffer->target_node;
tr.target.ptr = target_node->ptr;
tr.cookie = target_node->cookie;
// 后续 binder_transaction_data 发给 sever 使用的 binder protocol command 为 BR_TRANSACTION
cmd = BR_TRANSACTION;
tr.code = t->code;
tr.flags = t->flags;
tr.data_size = t->buffer->data_size;
tr.offsets_size = t->buffer->offsets_size;
// 这里的 buffer 指针已经指向 target_proc 的 binder_buffer
tr.data.ptr.buffer = (void *)t->buffer->data + proc->user_buffer_offset;
tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *));
// 将 cmd 和 binder_transacton_data 写到 server 的 mIn 中
put_user(cmd, (uint32_t __user *)ptr)
ptr += sizeof(uint32_t);
copy_to_user(ptr, &tr, sizeof(tr))
ptr += sizeof(tr);
// 将 t push 到 target thread 的 transaction_stack
t->to_parent = thread->transaction_stack;
t->to_thread = thread;
thread->transaction_stack = t;
// 之后 server 会从阻塞中返回
1.5. server 执行 transact
IPCThreadState::joinThreadPool
// cmd 此处应为 BR_BRANSACTION
cmd = mIn.readInt32();
executeCommand(cmd);
switch ((uint32_t)cmd):
case BR_TRANSACTION:
binder_transaction_data tr;
// 从 mIn 中读取 binder_transaction_data
result = mIn.read(&tr, sizeof(tr));
Parcel buffer;
// 这里的 tr.data.ptr.buffer 实际上位于 server 的 binder_buffer,
// 所以这里的 buffer 的 payload 是指向 binder_buffer 的 (而不是它自己 malloc 获得),
// 所以需要通过 ipcSetDataReference 将 freeBuffer 这个函数注册为 buffer 的 owner, 后续
// 释放这个 buffer 时会通过 freeBuffer 调到 binder driver 以释放 binder_buffer
buffer.ipcSetDataReference(
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
// reply 做为 server 使用的临时变量, 后续 server 返回到 binder driver 时, 这个 reply 的数据会
// 被传递给 driver
Parcel reply;
// tr.cookie 是 server 端 BBinder 的地址
sp<BBinder> b((BBinder*)tr.cookie);
// 终于, server 端的 transact 被调用
error = b->transact(tr.code, buffer, &reply, tr.flags);
1.6. server send reply
IPCThreadState::joinThreadPool
...
// 接上一步, b->transact 结束后, server 会执行 sendReply
// sendReply 的过程和 IPCTheadState::transact 基本是一样的.
sendReply(reply)
writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
waitForResponse(NULL, NULL);
1.7. binder driver 唤醒阻塞的 client
binder driver 唤醒 client 的过程与 client 唤醒阻塞的 server 的过程基本一致, 不同的是, 唤醒 client 需要根据 server binder_thread 的 transaction_stack 找到对应的 client 端的 binder_thread, 并唤醒它
binder_transaction
if (reply):
// thread->transaction_stack 在 server 的 binder_thread_read 时被 push
in_reply_to = thread->transaction_stack;
// to_parent 在 binder_thread_read 时被赋值为 server binder_thread 的 transaction_stack
thread->transaction_stack = in_reply_to->to_parent;
// from 在 binder_transaction 时被赋值为 client binder_thread
target_thread = in_reply_to->from;
// target_thread->wait (client binder_thread) 会被唤醒, 而不是 target_proc->wait
target_list = &target_thread->todo;
target_wait = &target_thread->wait;
wake_up_interruptible(target_wait);
1.8. client 的 binder_thread 被唤醒
与 server 的 binder_thread 被唤醒类似, 但是其 tr.cookie 设为 NULL (因为 client 并不需要这个值), 且返回的 cmd = BR_REPLY
1.9. client 拿到返回结果
IPCTheadState::waitForResponse(reply)
talkWithDriver()
cmd = (uint32_t)mIn.readInt32();
switch (cmd):
// 这里会读到 BR_REPLY
case BR_REPLY:
binder_transaction_data tr;
mIn.read(&tr, sizeof(tr));
reply->ipcSetDataReference(
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t),
freeBuffer, this);
1.10. binder driver 之外的流程
// java
BinderProxy.transact
// c++
BpBinder.transact
IPCTheadState.transact
// driver
binder_ioctl
// c++
IPCTheadState.getAndExecuteCommand
BBinder.transact
JavaBBinder.onTransact
// java
Binder.execTransact
Binder.onTransact
1.11. 总结
- parcel 数据的流向
- client
- parcel
- binder_transaction_data
- mOut
- binder_write_read
- mOut
- binder_transaction_data
- parcel
- driver
- client
- binder_transaction_data
- server binder_buffer
- binder_transaction
- server binder_buffer
- binder_transaction_data
- server
- binder_transaction_data
- mIn
- binder_transaction_data
- client
- server
- binder_transaction_data
- parcel
- binder_transaction_data
- client
- proc->wait/todo 与 thread->wait/todo 的区别
- proc->wait/todo 代表的是 target_proc 中那些 "looper binder_thread"
- thread->wait/todo 代表的是 client_proc 中"等待 transaction 返回" 的 binder_thread, 和 binder_transaction 有很大关系
- binder_thread 与 应用层的 binder thread #xxx 并非一一对应, binder thread #xxx 实际对应的是 (1) 中的 binder_thread, 还有一类 binder_thread 是 (2) 中的 binder_thread
- binder_transaction
调用序列
+-----------------------+--------------------------------------------------------+--------------------------------------------------+--------------+ | client | binder_thread_write | binder_thread_read | server | |-----------------------+--------------------------------------------------------+--------------------------------------------------+--------------| | 1. transact() | | | | | | 2. get binder_transaction_data from user mode | | | | | cmd=BC_TRANSACTION | | | | | construct binder_transaction and | | | | | put it to target's transaction_stack | | | | | add binder_work to target_proc->todo | | | | | wakup one of the server binder thread | | | | | | 3. get binder_work from proc->todo | | | | | get binder_transaction from | | | | | binder_work | | | | | construct binder_transaction_data for user mode | | | | | reset binder_transaction_data (reset binder,fd.. | | | | | and set cmd=BR_TRANSACTION | | | | | |4. transact ()| | | 5. get binder_transaction_data from user mode | | | | | cmd=BC_REPLY | | | | | get binder_transaction from it's own transaction_stack | | | | | so as to get target_thread | | | | | construct binder_transaction and add binder_work to | | | | | target_thread->todo, then wake up target thread | | | | | | 6. get binder_work from thread->todo | | | | | get binder_transaction | | | | | construct binder_transaction_data for user mode | | | | | reset binder_transaction_data | | | | | and set cmd=BR_REPLY | | | 7. transact() returns | | | | +-----------------------+--------------------------------------------------------+--------------------------------------------------+--------------+
另外, 需要注意的是, 上面的描述中忽略了和 BINDER_WORK_TRANSACTION_COMPLETE 相关的部分, 实际上 binder_thread_read 在阻塞在 thread->wait 时需要先返回上层然后重新调用 binder_thread_read 才能阻塞, 而不是直接阻塞. 具体参考 TF_ONE_WAY.
