Binder Overall

Table of Contents

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. 总结

  1. 将 parcel 中的 mData, mObjects 指针封装在 binder_transaction_data
  2. 将这个 binder_transaction_data 写入 mIn 这个 parcel 中
  3. 将 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. 总结

  1. parcel 数据的流向
    1. client
      • parcel
        • binder_transaction_data
          • mOut
            • binder_write_read
    2. driver
      1. client
        • binder_transaction_data
          • server binder_buffer
            • binder_transaction
      2. server
        • binder_transaction_data
          • mIn
    3. server
      • binder_transaction_data
        • parcel
  2. proc->wait/todo 与 thread->wait/todo 的区别
    1. proc->wait/todo 代表的是 target_proc 中那些 "looper binder_thread"
    2. thread->wait/todo 代表的是 client_proc 中"等待 transaction 返回" 的 binder_thread, 和 binder_transaction 有很大关系
    3. binder_thread 与 应用层的 binder thread #xxx 并非一一对应, binder thread #xxx 实际对应的是 (1) 中的 binder_thread, 还有一类 binder_thread 是 (2) 中的 binder_thread
  3. binder_transaction
  4. 调用序列

    +-----------------------+--------------------------------------------------------+--------------------------------------------------+--------------+
    | 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.

Author: [email protected]
Date: 2017-08-24 Thu 00:00
Last updated: 2019-12-31 Tue 18:10

知识共享许可协议