基于Linux内核3.18版本分析
1. 注册成为驱动的管理者
binder_get_thread:根据当前正在调用
ioctl()的 Linux 线程 ID(current->pid),去当前进程proc的threads红黑树里查找对应的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_size 和 read_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 的核心工作就是"找目标 → 放入目标 todo → wake_up → 取出 transaction → 转成 BR_TRANSACTION 交给用户空间";而 ServiceManager 真正的业务处理,则发生在 BR_TRANSACTION 返回用户空间之后。