从驱动来分析服务的添加过程

基于Linux内核3.18版本分析

1. 注册成为驱动的管理者

binder_get_thread:根据当前正在调用 ioctl() 的 Linux 线程 ID(current->pid),去当前进程 procthreads 红黑树里查找对应的 binder_thread;如果没有,就创建一个。

获取判断 binder 驱动版本号是否一致:

c 复制代码
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int ret;
    struct binder_proc *proc = filp->private_data;
    struct binder_thread *thread;
    void __user *ubuf = (void __user *)arg;
    // 查询获取一个 binder_thread 
    thread = binder_get_thread(proc);
    if (thread == NULL) {
        ret = -ENOMEM;
        goto err;
    }

    switch (cmd) {
        case BINDER_VERSION: {
            struct binder_version __user *ver = ubuf;
            // 把版本号拷贝给 sm 进程  
            if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
                     &ver->protocol_version)) {
                ret = -EINVAL;
                goto err;
            }
            break;
        }
            return ret;
    }
c 复制代码
static struct binder_thread *binder_get_thread(struct binder_proc *proc)
{
    struct binder_thread *thread = NULL;
    struct rb_node *parent = NULL;
    struct rb_node **p = &proc->threads.rb_node;

    while (*p) {
        parent = *p;
        // 通过 rb_node 去找到 binder_thread
        thread = rb_entry(parent, struct binder_thread, rb_node);

        if (current->pid < thread->pid)
            p = &(*p)->rb_left;
        else if (current->pid > thread->pid)
            p = &(*p)->rb_right;
        else
            break;
    }
    if (*p == NULL) {
        // 第一次 pid 创建对象
        thread = kzalloc(sizeof(*thread), GFP_KERNEL);
        if (thread == NULL)
            return NULL;
        binder_stats_created(BINDER_STAT_THREAD);
        thread->proc = proc;
        // current 线程
        thread->pid = current->pid;
        // 初始化等待队列和工作队列
        init_waitqueue_head(&thread->wait);
        INIT_LIST_HEAD(&thread->todo);
        // 插入到当前进程线程列表的红黑树
        rb_link_node(&thread->rb_node, parent, p);
        // 调整红黑树的颜色
        rb_insert_color(&thread->rb_node, &proc->threads);
    }
    return thread;
}
  • struct binder_proc *proc = filp->private_data

这里的 proc 表示:当前打开 /dev/binder 的进程在 Binder 驱动中的内核对象。一个进程对应一个 binder_proc

  • proc->threads

这是一个红黑树,用来保存这个进程已经在 Binder 驱动中注册过的 binder_thread

类似:

text 复制代码
binder_proc
    │
    └── threads 红黑树
          │
          ├── binder_thread  pid=1001
          ├── binder_thread  pid=1005
          ├── binder_thread  pid=1010
          └── ...
  • current

是当前正在执行内核代码的进程/线程对应的 task_struct。 current->pid 就是当前调用 ioctl()线程 ID

  • thread = rb_entry(parent, struct binder_thread, rb_node)

rb_node 本身只是 Linux 红黑树节点: struct rb_node ,它并不是: struct binder_thread ,但是 binder_thread 里面嵌入了: struct rb_node rb_node ,所以说它们是包含关系,可以根据 rb_node 的地址,反推出它所属的 binder_thread 对象。

c 复制代码
binder_thread
┌──────────────────────────┐
│ proc                      │
│ pid = 1001                │
│ wait                      │
│ todo                      │
│                           │
│ rb_node ◄─────────────────┤── 当前红黑树节点
└──────────────────────────┘

后面代码就是先从红黑树中去查找,找不到?然后根据当前进程的proc 和线程ID current->pid 以及 初始化等待队列和工作队列去创建一个新的 binder_thread ,然后把它插入到当前的 rb_node 红黑树中。

所以 binder_get_thread(proc) 干的事情就是:我现在是这个进程里的哪个线程?这个线程有没有对应的 binder_thread?有就返回,没有就创建。


让 ServiceManager 进程成为管理者:

c 复制代码
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    switch (cmd) {
        case BINDER_SET_CONTEXT_MGR:
            ret = binder_ioctl_set_ctx_mgr(filp);
            if (ret)
                goto err;
            break;
    }
}

static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
    int ret = 0;
    struct binder_proc *proc = filp->private_data;
    kuid_t curr_euid = current_euid();
    // 静态 binder_context_mgr_node 是否有设置过,只能有一个应用成为管理者
    if (binder_context_mgr_node != NULL) {
        pr_err("BINDER_SET_CONTEXT_MGR already set\n");
        ret = -EBUSY;
        goto out;
    }
    // 创建一个单独 binder_node
    binder_context_mgr_node = binder_new_node(proc, 0, 0);
    out:
    return ret;
}
c 复制代码
static struct binder_node *binder_new_node(struct binder_proc *proc,
             binder_uintptr_t ptr,
             binder_uintptr_t cookie)
{
    // proc->nodes.rb_node 代表的是内部(本)进程的 Binder 对象的红黑树
  struct rb_node **p = &proc->nodes.rb_node;
  struct rb_node *parent = NULL;
  struct binder_node *node;

  while (*p) {
    parent = *p;
    node = rb_entry(parent, struct binder_node, rb_node);

    if (ptr < node->ptr)
      p = &(*p)->rb_left;
    else if (ptr > node->ptr)
      p = &(*p)->rb_right;
    else
      return NULL;
  }

  node = kzalloc(sizeof(*node), GFP_KERNEL);
  if (node == NULL)
    return NULL;
    // 把创建的 binder_node 添加到红黑树 
  rb_link_node(&node->rb_node, parent, p);
  rb_insert_color(&node->rb_node, &proc->nodes);
    // 设置一些参数  
    node->proc = proc;
  node->ptr = ptr;
  node->cookie = cookie;
  node->work.type = BINDER_WORK_NODE;
  INIT_LIST_HEAD(&node->work.entry);
  INIT_LIST_HEAD(&node->async_todo);
  return node;
}

BINDER_SET_CONTEXT_MGR 的本质就是:当前进程通过 Binder 驱动把自己注册成这个 Binder 域唯一的 Context Manager,驱动把它对应的 binder_node 保存到全局 binder_context_mgr_node 中;以后其他进程通过特殊的 handle=0 就能找到这个管理者。

与其他binder_node 的关系如下所示:

c 复制代码
                  Binder Driver
                       │
          ┌────────────┴────────────┐
          │                         │
          ▼                         ▼
binder_context_mgr_node       普通 binder_node
          │                         │
          ▼                         ▼
  ServiceManager              system_server
  Context Manager             各种系统服务
          │
          │ 保存服务注册表
          ▼
  "activity" → ActivityManager
  "package"  → PackageManager
  "window"   → WindowManager

2. 进程进入循环等待

ServiceManager 自己没有业务请求时,就把 Binder 线程挂到 Binder Driver 的等待队列上睡眠;其他进程通过 Binder 发起 addService() / getService() 等请求后,Binder Driver 把请求放入 ServiceManager 对应线程的 todo 队列并唤醒它。

c 复制代码
for (;;) {
    bwr.read_size = sizeof(readbuf);
    bwr.read_consumed = 0;
    bwr.read_buffer = (uintptr_t) readbuf;

    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
}

BINDER_WRITE_READ 之所以叫 WRITE_READ 是因为它能完成两个方向的工作,读写都靠它:

c 复制代码
struct binder_write_read {
    binder_size_t write_size;
    binder_size_t write_consumed;
    binder_uintptr_t write_buffer;

    binder_size_t read_size;
    binder_size_t read_consumed;
    binder_uintptr_t read_buffer;
};

这里主要看 read_sizeread_buffer

就是告诉Binder Driver,如果你这里有工作,把数据写到我用户空间的 readbuf 里面。

所以 ioctl(bs->fd, BINDER_WRITE_READ, &bwr); 进入内核:binder_ioctl_write_read()

c 复制代码
static int binder_ioctl_write_read(struct file *filp,
                                   unsigned int cmd, unsigned long arg,
                                   struct binder_thread *thread)
{
    int ret = 0;
    struct binder_proc *proc = filp->private_data;
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
    struct binder_write_read bwr;

    // 把数据 ubuf(binder_write_read) 复制到 bwr
    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }

    if (bwr.write_size > 0) {
        ...
        }
    if (bwr.read_size > 0) {
        ret = binder_thread_read(proc, thread, bwr.read_buffer,
                                 bwr.read_size,
                                 &bwr.read_consumed,
                                 filp->f_flags & O_NONBLOCK);
    }
    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
    out:
    return ret;
}

binder_ioctl_write_read() 是 Binder Driver 处理用户空间 BINDER_WRITE_READ 请求的入口,而真正决定"有没有工作、是否睡眠"的关键函数就是 binder_thread_read()

binder_thread_read:

c 复制代码
static int binder_thread_read(struct binder_proc *proc,
                              struct binder_thread *thread,
                              binder_uintptr_t binder_buffer, size_t size,
                              binder_size_t *consumed, int non_block)
{
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    int ret = 0;
    int wait_for_proc_work;

    retry:

    // 第一次是true
    wait_for_proc_work = thread->transaction_stack == NULL &&
        list_empty(&thread->todo);
    
    // 改变状态
    thread->looper |= BINDER_LOOPER_STATE_WAITING;
    if (wait_for_proc_work)
        proc->ready_threads++;

    binder_unlock(__func__);

    if (wait_for_proc_work) {
        // 非阻塞
        if (non_block) {
            ...
            } else
            // 要不要进入等待,servicemanager 正在进入等待状态,什么时候被唤醒,就是 todo 队列里面有东西
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    } else {
        ...
        }

    binder_lock(__func__);
    
    if (wait_for_proc_work)
    proc->ready_threads--;
    thread->looper &= ~BINDER_LOOPER_STATE_WAITING;

    while (1) {

    // Binder Driver 判断 binder_has_proc_work() 成立后唤醒线程,
    // 线程醒来后再从 thread->todo / proc->todo 等工作队列取工作。
    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 {
        ...
        goto retry;
    }

    //BINDER_WORK_TRANSACTION
    //cmd = BR_TRANSACTION
    //copy_to_user 返回用户空间
    // 下一轮循环...
    return 0;
}
c 复制代码
    wait_for_proc_work = thread->transaction_stack == NULL &&
        list_empty(&thread->todo);

首先判断:当前线程有没有自己的工作,因为是第一次进来 todo 肯定是空的,任务栈也是NULL

当前线程没有事务栈,也没有自己的待处理任务,因此可以去等待进程级 Binder 工作。

c 复制代码
thread->looper |= BINDER_LOOPER_STATE_WAITING;

告诉 Binder Driver:这个线程已经进入等待状态。

这个状态很重要,因为 Binder Driver 后面在分发事务、唤醒线程的时候,需要知道:有没有 Binder 线程正在空闲等待?

c 复制代码
    if (wait_for_proc_work)
        proc->ready_threads++;

当前这个 Binder 进程中,有多少 Binder 线程处于"可以接收工作"的状态。

意味着:ServiceManager 进程现在有一个 Binder 线程空闲,可以接收 Binder 工作。

c 复制代码
binder_unlock(__func__);

先释放 Binder 锁,为后续的线程睡眠做准备,以便让其他进程可以进入Binder Driver,否则带着锁睡眠整个 Binder 系统就可能被卡住。

c 复制代码
wait_event_freezable_exclusive

可以简单理解为 只要 Binder Driver 判断 ServiceManager 当前没有工作,就让 ServiceManager 线程睡眠。

后续就是被唤醒后,加锁,从todo队列中拿到数据处理,最后通过 cmd = BR_TRANSACTION 返回到用户空间,再进入下次一次循环...

注意:此时的事务还是在内核空间中,返回的是 ServiceManager的用户空间。

3. 唤醒目标服务进程

Binder 所谓"唤醒目标服务进程",本质上就是:把 binder_transaction 放入目标进程的 todo 队列,然后 wake_up(target_wait)

c 复制代码
static int binder_thread_write(struct binder_proc *proc,
                               struct binder_thread *thread,
                               binder_uintptr_t binder_buffer, size_t size,
                               binder_size_t *consumed)
{
    uint32_t cmd;
    // write_buffer , cmd, binder_transaction_data 
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    while (ptr < end && thread->return_error == BR_OK) {
        if (get_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);

        switch (cmd) {
                // 进入这里
            case BC_TRANSACTION:
            case BC_REPLY: {
                struct binder_transaction_data tr;
                // 把 ptr(binder_transaction_data) 复制到 tr
                if (copy_from_user(&tr, ptr, sizeof(tr)))
                    return -EFAULT;
                ptr += sizeof(tr);
                binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
                break;
            }
        }
        return 0;
    }

binder_thread_write() 自己不负责发送事务,它只是负责解析用户空间传来的 Binder 命令,然后调用 binder_transaction() 真正执行事务发送。

c 复制代码
case BC_TRANSACTION:
case BC_REPLY:

这里两个命令共用,功能都是要让 Binder Driver 把一段 transaction 数据交给另一个 Binder 线程。区别是一个是普通的请求另一个是之前请求的回复。

进入 binder_transaction()

c 复制代码
static void binder_transaction(struct binder_proc *proc,
                               struct binder_thread *thread,
                               struct binder_transaction_data *tr, int reply)
{
    struct binder_transaction *t;
    struct binder_work *tcomplete;
    binder_size_t *offp, *off_end;
    // 找到目标进程的 binder_proc 
    struct binder_proc *target_proc;
    // 找到目前进程的 binder_thread 
    struct binder_thread *target_thread = NULL;
    // 目标进程的 binder_node 
    struct binder_node *target_node = NULL;
    // 目标进程的 todo 队列
    struct list_head *target_list;
    // 目标进程的等待

    wait_queue_head_t *target_wait;
    struct binder_transaction *in_reply_to = NULL;
    struct binder_transaction_log_entry *e;
    // handle 0 
    e->target_handle = tr->target.handle;
    e->data_size = tr->data_size;
    e->offsets_size = tr->offsets_size;

    if (reply) {
        ... 
        } else {
        if (tr->target.handle) {

        } else {
            // 0 , target_node 是之前 servicemanager 成为管理者的静态变量
            target_node = binder_context_mgr_node;
        }
        // 获取到了 servicemanager 进程的 binder_proc 
        target_proc = target_node->proc;
    }
    if (target_thread) {
        ...
        } else {
        // 获取到目前的等待和工作队列
        target_list = &target_proc->todo;
        target_wait = &target_proc->wait;
    }

    /* TODO: reuse incoming transaction for reply */
    // 创建了两个结构体
    t = kzalloc(sizeof(*t), GFP_KERNEL);
    tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
    // 记录从哪里来的

    if (!reply && !(tr->flags & TF_ONE_WAY))
        t->from = thread;
    else
        ...
        // 按需加载在目标进程开辟管理映射内存,我们在 mmap 的时候只映射了一个物理页,现在你穿的数据有多大就开辟多大
        t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
    if (t->buffer == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_binder_alloc_buf_failed;
    }
    t->buffer->allow_user_free = 0;
    t->buffer->debug_id = t->debug_id;
    t->buffer->transaction = t;
    t->buffer->target_node = target_node;


    offp = (binder_size_t *)(t->buffer->data +

                             ALIGN(tr->data_size, sizeof(void *)));
    // 把数据拷贝到目标进程,t->buffer->data(新开辟的目标进程的地址),tr->data.ptr.buffer (是客户端的数据)
    if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
                       tr->data.ptr.buffer, tr->data_size)) {
        binder_user_error("%d:%d got transaction with invalid data ptr\n",
                          proc->pid, thread->pid);
        return_error = BR_FAILED_REPLY;
        goto err_copy_data_failed;
    }
    if (copy_from_user(offp, (const void __user *)(uintptr_t)
                       tr->data.ptr.offsets, tr->offsets_size)) {
        binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
                          proc->pid, thread->pid);
        return_error = BR_FAILED_REPLY;
        goto err_copy_data_failed;
    }
    // 循环处理客户端传递的 flat_binder_object
    off_end = (void *)offp + tr->offsets_size;
    for (; offp < off_end; offp++) {
        struct flat_binder_object *fp;
        fp = (struct flat_binder_object *)(t->buffer->data + *offp);
        switch (fp->type) {
            case BINDER_TYPE_BINDER:
            case BINDER_TYPE_WEAK_BINDER: {
                struct binder_ref *ref;
                // 通过客户端的 binder 地址从自己的进程中找 binder_node 
                struct binder_node *node = binder_get_node(proc, fp->binder);

      if (node == NULL) {
        node = binder_new_node(proc, fp->binder, fp->cookie);
      }
      if (fp->cookie != node->cookie) {
        ...
      }
            // 从目标进程里面获取 binder_node 
      ref = binder_get_ref_for_node(target_proc, node);
      if (ref == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_binder_get_ref_for_node_failed;
      }
            // 替换 type = BINDER_TYPE_HANDLE
      if (fp->type == BINDER_TYPE_BINDER)
        fp->type = BINDER_TYPE_HANDLE;
      else
        ...
            // 计算获取 handle 

      fp->handle = ref->desc;
    } break;
  }
  if (reply) {
        ...

   } else if (!(t->flags & TF_ONE_WAY)) {
        // 不是 one_way 

    BUG_ON(t->buffer->async_transaction != 0);
        // 需要回复

    t->need_reply = 1;
        // 记录了 from_parent 
    t->from_parent = thread->transaction_stack;
        // 记录了 transaction_stack  
    thread->transaction_stack = t;
  } else {
    
  }
    // 目标,往目标进程todo队列塞了一个 &t->work.entry
  t->work.type = BINDER_WORK_TRANSACTION;
  list_add_tail(&t->work.entry, target_list);
    // 本进程,往本进程塞了一个 &tcomplete->entry
  tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
  // 发送方的线程投递完成后会收到 BR_TRANSACTION_COMPLETE
  list_add_tail(&tcomplete->entry, &thread->todo);
    // 唤醒 target_wait
  if (target_wait)
    wake_up_interruptible(target_wait);
  return;
}
c 复制代码
static void binder_transaction(
        struct binder_proc *proc, // 发送方进程
        struct binder_thread *thread, // 发送方线程
        struct binder_transaction_data *tr, // 用户空间传来的 transaction 描述
        int reply)// 是不是 BC_REPLY , 对于主动请求 此时为false

找到目标进程,前面可以知道 ServiceManager 将自己设置为上下文管理者Context Manager,也就是 ServiceManager 对应的 Binder Node。

c 复制代码
if (reply) {
    ...
} else {
    if (tr->target.handle) {
        ...
    } else {

        target_node = binder_context_mgr_node;
    }
    target_proc = target_node->proc;
}

容易混淆的结构:

  • binder_proc:代表一个进程
  • binder_thread:代表这个进程中的某个 Binder 线程
  • binder_node:代表一个可以被 Binder 引用的 Binder 对象

所以: target_node->proc 就是:通过目标 Binder 对象找到它属于哪个进程。

c 复制代码
if (target_thread) {
    ...
} else {

    target_list = &target_proc->todo;
    target_wait = &target_proc->wait;
}

如果 Binder Driver 没有指定某一个特定的目标线程:target_thread == NULL

就会把事务先放到目标进程级别的 todo 队列。

因为上面 ServiceManager线程是在 proc->wait 上等待的,也与上面对应:

c 复制代码
wait_event_freezable_exclusive(
    proc->wait,
    binder_has_proc_work(proc, thread));

接下来创建 binder_transaction

c 复制代码
struct binder_transaction *t;
...
t = kzalloc(sizeof(*t), GFP_KERNEL);

记录从哪里来:

c 复制代码
if (!reply && !(tr->flags & TF_ONE_WAY))
    t->from = thread; // Client Thread

也就是 Binder Driver 把:这是谁发来的?记录在 binder_transaction 了。

因为 ServiceManager 回复的时候(BC_REPLY),Binder Driver 要知道:回复应该送回哪个线程?

给目标进程分配 Binder Buffer

c 复制代码
t->buffer = binder_alloc_buf(
        target_proc,
        tr->data_size,
        tr->offsets_size,
        ...
);

这个 Buffer 是在目标进程的 Binder 内存管理体系中分配的。

就是ServiceManager mmap 的 Binder 地址空间,分配一块新的 transaction buffer

然后把 Client 的数据复制到目标 Buffer

c 复制代码
copy_from_user(
    t->buffer->data,
    (const void __user *)(uintptr_t)
        tr->data.ptr.buffer,
    tr->data_size);

把 数据 tr->data.ptr.buffer (Client 用户空间)拷贝到 t->buffer->data (ServiceManager Binder Buffer)

BINDER_TYPE_BINDER 转成 BINDER_TYPE_HANDLE

c 复制代码
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {

    struct binder_ref *ref;

    struct binder_node *node =
        binder_get_node(proc, fp->binder);

    if (node == NULL) {
        node = binder_new_node(
            proc,
            fp->binder,
            fp->cookie);
    }
    ref = binder_get_ref_for_node(
        target_proc,
        node);
    ...
    fp->type = BINDER_TYPE_HANDLE;
    fp->handle = ref->desc;
}

因为:Binder 对象不能直接把一个进程里的指针给另一个进程使用。

也就是:

c 复制代码
Client中的Binder对象
        ↓
Binder Driver建立跨进程引用
        ↓
ServiceManager得到handle

然后设置同步事务关系

c 复制代码
t->need_reply = 1;

t->from_parent = thread->transaction_stack;

thread->transaction_stack = t;

Binder Driver 在发送端保存:当前 transaction,等 BC_REPLY的时候可以通过事务找到原来的Client Thread。

把 transaction 放进目标队列

c 复制代码
target_list = &target_proc->todo;
...
t->work.type = BINDER_WORK_TRANSACTION;

list_add_tail(
    &t->work.entry,
    target_list);

把刚刚创建的 Binder transaction 放到 ServiceManager 进程的 todo 队列。

c 复制代码
ServiceManager proc
       │
       └── todo
             │
             └── binder_transaction

唤醒目标进程

唤醒正在 target_proc->wait 上睡眠的 Binder 线程。

c 复制代码
target_wait = &target_proc->wait;
...

if (target_wait)
    wake_up_interruptible(target_wait);

正好对应上前面的睡眠:

c 复制代码
wait_event_freezable_exclusive(
    proc->wait,
    binder_has_proc_work(proc, thread));
c 复制代码
发送方                                      ServiceManager
────────────────────────────────────────────────────────────

binder_transaction()
       │
       │
       ├── target_proc = ServiceManager
       │
       ├── target_list = &target_proc->todo
       │
       ├── 创建 t
       │
       ├── 复制 transaction data
       │
       ├── list_add_tail()
       │       │
       │       ▼
       │   ServiceManager
       │       proc->todo
       │
       └── wake_up()
               │
               ▼
                                      wait_event(...)
                                           │
                                           │ 被唤醒
                                           ▼
                                      binder_thread_read()
                                           │
                                           ▼
                                      从 proc->todo
                                      取出 t
                                           │
                                           ▼
                                      BR_TRANSACTION

所以可以把 Binder 唤醒机制直接理解为:

发送方把 transaction 塞进目标 binder_proc->todo,然后唤醒目标 binder_proc->wait;目标 Binder 线程从睡眠中醒来,再从 todo 中取出这个 transaction,最终转换成 BR_TRANSACTION 返回到目标进程用户空间。

4. 进程唤醒处理数据

binder_transaction 负责:把事务送进去,binder_thread_read 负责:把事务取出来交给用户空间。

c 复制代码
static int binder_thread_read(struct binder_proc *proc,
                              struct binder_thread *thread,
                              binder_uintptr_t binder_buffer, size_t size,
                              binder_size_t *consumed, int non_block)
{

    ...
    
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    if (ret)
        return ret;

    // 被唤醒后 进入
    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;

        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 {

        }

        if (end - ptr < sizeof(tr) + 4)
            break;

        switch (w->type) {
            case BINDER_WORK_TRANSACTION: {
                t = container_of(w, struct binder_transaction, work);
            } break;
        }

        if (!t)
            continue;

        BUG_ON(t->buffer == NULL);
        if (t->buffer->target_node) {
            struct binder_node *target_node = t->buffer->target_node;

            tr.target.ptr = target_node->ptr;// 0
            tr.cookie =  target_node->cookie;// 0
            cmd = BR_TRANSACTION;
        } else {

        }
        tr.code = t->code;


        tr.data_size = t->buffer->data_size;
        tr.offsets_size = t->buffer->offsets_size;
        tr.data.ptr.buffer = (binder_uintptr_t)(
            (uintptr_t)t->buffer->data +
            proc->user_buffer_offset);
        tr.data.ptr.offsets = tr.data.ptr.buffer +
            ALIGN(t->buffer->data_size,
                          sizeof(void *));
        // 首先把 命令 BR_TRANSACTION 写到 ptr ,readbuffer 
        if (put_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        // 把数据原样不动拷贝到 readbuffer 
        if (copy_to_user(ptr, &tr, sizeof(tr)))
            return -EFAULT;
        ptr += sizeof(tr);

        list_del(&t->work.entry);
        t->buffer->allow_user_free = 1;
        if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
            t->to_parent = thread->transaction_stack;
            t->to_thread = thread;
            thread->transaction_stack = t;
        } else {

        }
        break;
    }
    return 0;
}

Binder 有两级工作队列:

c 复制代码
                    binder_proc
                       │
              ┌────────┴────────┐
              │                 │
         proc->todo          binder_thread
                               │
                               └── thread->todo

第一优先级 thread->todo 当前线程自己的工作。

第二优先级 proc->todo 当前进程级别的工作。

所以对于我们现在的场景,ServiceManager 被唤醒之后是处理进程级别的工作。

获取 Binder Transaction

c 复制代码
t = container_of(w,
                 struct binder_transaction,
                 work);

因为 binder_work 是被包含在 binder_transaction 结构体中的,所以此处可以从binder_work * 反推出 binder_transaction *

c 复制代码
binder_transaction
┌─────────────────────────┐
│ transaction相关字段      │
│                         │
│ struct binder_work work │ ← w指向这里
│                         │
└─────────────────────────┘
c 复制代码
ServiceManager
      │
      ▼
proc->todo
      │
      ▼
binder_work
      │
      ▼
container_of()
      │
      ▼
binder_transaction *t

这个 t 就是:Client 发给 ServiceManager 的完整 Binder 事务在内核中的描述对象。

因为是请求所以 cmd = BR_TRANSACTION;

c 复制代码
if (t->buffer->target_node) {

    struct binder_node *target_node =
        t->buffer->target_node;

    tr.target.ptr = target_node->ptr;
    tr.cookie = target_node->cookie;

    cmd = BR_TRANSACTION;

} else {

    cmd = BR_REPLY;
}

内核中的 binder_transaction 到这里,被转换成了用户空间 Binder 循环能够理解的 BR_TRANSACTION

然后告诉用户空间:数据在哪里?

告诉 ServiceManager:真正的 Binder 数据位于哪个用户空间地址。

c 复制代码
tr.data_size =
    t->buffer->data_size;

tr.offsets_size =
    t->buffer->offsets_size;

tr.data.ptr.buffer =
    (binder_uintptr_t)(
        (uintptr_t)t->buffer->data +
        proc->user_buffer_offset);

tr.data.ptr.offsets =
    tr.data.ptr.buffer +
    ALIGN(
        t->buffer->data_size,
        sizeof(void *)
    );

t->buffer->data 能给 ServiceManager 用户空间使用的原因是

前面在上一步发送端的 binder_transaction() 方法中已经在目标进程开辟出了空间

c 复制代码
binder_alloc_buf(
    target_proc,
    ...
);

是在:对应的 Binder 内存区域里分配 Buffer。

然后 Binder Driver 把这个 Buffer 的用户空间地址告诉 ServiceManager: tr.data.ptr.buffer = ...

因此 ServiceManager 收到 BR_TRANSACTION 后,可以根据: tr.data.ptr.buffer ,直接访问这块 Binder Buffer。

copy_to_user()

这里拷贝的是 binder_transaction_data,而不是整个 Parcel 。

这里 copy_to_user() 主要是把:

c 复制代码
BR_TRANSACTION
+
binder_transaction_data

写到 ServiceManager 的 readbuf。把: binder_transaction_data tr 写到用户空间。

然后把 transaction 从 todo 队列删除:

c 复制代码
list_del(&t->work.entry);
c 复制代码
之前:

proc->todo
    │
    └── t

现在:

proc->todo
    │
    └── empty

因为:这个 transaction 已经被 Binder 线程取出来准备交给用户空间了。所以不能继续留在 todo

transaction_stack

c 复制代码
if (cmd == BR_TRANSACTION &&
    !(t->flags & TF_ONE_WAY)) {

    t->to_parent =
        thread->transaction_stack;

    t->to_thread =
        thread;

    thread->transaction_stack = t;
}

对于同步事务,ServiceManager 处理完毕需要通过 t 去回复。和上面发送时一样 Binder Driver 需要知道:这个 reply 对应的是哪个请求?原来的调用线程是谁?

全部完整流程:

c 复制代码
                         Android Binder 核心流程
┌─────────────────────────────────────────────────────────────────────────────┐
│                                                                             │
│  ① ServiceManager 注册成为 Binder Context Manager(驱动管理者)             │
│                                                                             │
│  ServiceManager 用户空间                                                    │
│        │                                                                    │
│        │ ioctl(BINDER_SET_CONTEXT_MGR)                                      │
│        ▼                                                                    │
│  binder_ioctl()                                                             │
│        │                                                                    │
│        ▼                                                                    │
│  binder_ioctl_set_ctx_mgr()                                                 │
│        │                                                                    │
│        ├── 创建/设置 binder_context_mgr_node                                │
│        │                                                                    │
│        └── binder_context_mgr_node->proc = ServiceManager binder_proc       │
│                                                                             │
│                    ★ ServiceManager 成为 Binder 管理者                      │
│                                                                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ② ServiceManager 进入 Binder 循环等待                                      │
│                                                                             │
│  ServiceManager 用户空间                                                    │
│        │                                                                    │
│        │ BC_ENTER_LOOPER                                                    │
│        ▼                                                                    │
│  ioctl(BINDER_WRITE_READ)                                                    │
│        │                                                                    │
│        ▼                                                                    │
│  binder_ioctl_write_read()                                                   │
│        │                                                                    │
│        ▼                                                                    │
│  binder_thread_read()                                                        │
│        │                                                                    │
│        ├── 检查 thread->todo / proc->todo                                    │
│        │                                                                    │
│        ├── 当前没有事务                                                      │
│        │                                                                    │
│        ▼                                                                    │
│  wait_event_freezable_exclusive()                                            │
│        │                                                                    │
│        ▼                                                                    │
│                  ServiceManager Binder线程                                  │
│                         │                                                   │
│                         │ 睡眠等待                                          │
│                         ▼                                                   │
│                    proc->wait                                               │
│                                                                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ③ 客户端发送事务 → 唤醒 ServiceManager                                     │
│                                                                             │
│  Client 用户空间                                                            │
│        │                                                                    │
│        │ BC_TRANSACTION                                                     │
│        │ binder_transaction_data                                            │
│        ▼                                                                    │
│  ioctl(BINDER_WRITE_READ)                                                    │
│        │                                                                    │
│        ▼                                                                    │
│  binder_ioctl_write_read()                                                   │
│        │                                                                    │
│        ▼                                                                    │
│  binder_thread_write()                                                       │
│        │                                                                    │
│        │ 解析 BC_TRANSACTION                                                 │
│        ▼                                                                    │
│  binder_transaction()                                                        │
│        │                                                                    │
│        ├── 根据 handle / target 找目标 binder_node                           │
│        │                                                                    │
│        ├── target_node->proc                                                │
│        │       ↓                                                             │
│        │   ServiceManager binder_proc                                        │
│        │                                                                    │
│        ├── binder_alloc_buf()                                                │
│        │       ↓                                                             │
│        │   分配目标进程 Binder Buffer                                         │
│        │                                                                    │
│        ├── copy_from_user()                                                  │
│        │       ↓                                                             │
│        │   Client数据 → Binder Buffer                                       │
│        │                                                                    │
│        ├── 处理 Binder Object / Handle                                       │
│        │                                                                    │
│        ├── list_add_tail(&t->work.entry, &target_proc->todo)                 │
│        │                                                                    │
│        └── wake_up_interruptible(&target_proc->wait)                        │
│                              │                                              │
│                              │ ★ 唤醒                                        │
│                              ▼                                              │
│                    ServiceManager Binder线程                                │
│                              │                                              │
│                              ▼                                              │
│                         被唤醒继续执行                                       │
│                                                                             │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ④ ServiceManager 被唤醒 → 取出事务 → 返回 BR_TRANSACTION                   │
│                                                                             │
│  binder_thread_read()                                                        │
│        │                                                                    │
│        ├── 从 thread->todo / proc->todo 获取 binder_work                    │
│        │                                                                    │
│        ▼                                                                    │
│  container_of()                                                              │
│        │                                                                    │
│        ▼                                                                    │
│  binder_transaction *t                                                      │
│        │                                                                    │
│        ├── t->buffer                                                         │
│        ├── t->code                                                           │
│        ├── data_size                                                        │
│        └── offsets_size                                                     │
│                                                                             │
│        │                                                                    │
│        ▼                                                                    │
│  组装 binder_transaction_data tr                                             │
│        │                                                                    │
│        ├── cmd = BR_TRANSACTION                                              │
│        ├── tr.code                                                           │
│        ├── tr.data_size                                                      │
│        ├── tr.offsets_size                                                   │
│        └── tr.data.ptr.buffer                                                │
│                                                                             │
│        │                                                                    │
│        ├── put_user(BR_TRANSACTION, readbuf)                                │
│        │                                                                    │
│        └── copy_to_user(readbuf, &tr, sizeof(tr))                           │
│                                                                             │
│        ▼                                                                    │
│  ioctl(BINDER_WRITE_READ) 返回用户空间                                      │
│        │                                                                    │
│        ▼                                                                    │
│  ServiceManager 用户空间                                                     │
│        │                                                                    │
│        ▼                                                                    │
│  IPCThreadState::executeCommand()                                            │
│        │                                                                    │
│        └── case BR_TRANSACTION                                               │
│                    │                                                        │
│                    ▼                                                        │
│             ServiceManager Binder对象                                        │
│                    │                                                        │
│                    ▼                                                        │
│             onTransact() / 业务处理                                          │
│                    │                                                        │
│             ┌──────┴────────┐                                               │
│             │               │                                               │
│         getService()    addService()                                         │
│             │               │                                               │
│             └──────┬────────┘                                               │
│                    ▼                                                        │
│                构造 reply                                                     │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

5. 最后

Binder Driver 的核心工作就是"找目标 → 放入目标 todowake_up → 取出 transaction → 转成 BR_TRANSACTION 交给用户空间";而 ServiceManager 真正的业务处理,则发生在 BR_TRANSACTION 返回用户空间之后。

相关推荐
Dovis(誓平步青云)3 小时前
折叠屏悬停看视频,上半屏和下半屏应该各做什么
android·java·服务器·开发语言·安全·音视频
alexhilton4 小时前
让架构边界变成可执行的测试
android·kotlin·android jetpack
hai_android5 小时前
SendChannel 与 ReceiveChannel 通信机制
android
数据治理自习室7 小时前
AI 应用评测体系(GraphRAG)
android·大数据·人工智能·kotlin
爱笑鱼8 小时前
Android 系统启动机制(五):system_server 是 init 启动的,还是 Zygote fork 出来的?
android
智购科技无人售货机工厂9 小时前
2026自动售货机防拆机物理安全设计:从安全螺丝到结构互锁的工程实践~YH
android·网络·驱动开发·python·单片机·安全·云原生
开开心心就好9 小时前
电子教鞭工具支持画框写字插图片功能齐全
android·开发语言·前端·javascript·人工智能·pdf·html
Dovis(誓平步青云)9 小时前
拍视频前先把镜头想清楚:做一个分镜取景辅助器
android·java·服务器·javascript·人工智能
峥嵘life10 小时前
Android16 系统 APEX 模块说明
android·大数据·开发语言