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.