Binder事务发送
binder_transaction() 是 Binder 驱动把一条 BC_TRANSACTION 或 BC_REPLY 变成目标进程 work 的主函数。它不是简单的“复制数据”:函数必须先确定目标 node/proc/thread,再为目标 allocator 建立 buffer,校验并翻译 offsets、Binder object、fd 和 pointer object,最后按同步或 oneway 语义排队。
本文面向已经读过 BINDER_WRITE_READ命令、Binder数据转换链、事务数据生命周期 的读者,回答一次事务从 BC descriptor 到目标 work 的源码路径。
本文不重复讲完整的 BC/BR 字节循环,不把 binder_node、binder_ref 或 allocator 拆成独立参考手册;这些对象只在事务主线真正改变 owner 或状态的位置出现。事务成功不等于服务已执行,成功发送只表示目标 work 已排队或进入 pending 状态。
1. 两种方向
| 输入 | reply | 目标来源 | 发送方状态 |
|---|---|---|---|
BC_TRANSACTION | 0 | handle 或 context manager node | 同步请求入 transaction stack;oneway 不入 |
BC_REPLY | 1 | 当前线程 transaction stack 的原发送线程 | 弹出当前 reply 栈项 |
2. 事务对象
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()
static void binder_transaction(
struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr,
int reply,
binder_size_t extra_buffers_size) {
struct binder_transaction *t;
struct binder_work *w;
struct binder_work *tcomplete;
struct binder_proc *target_proc = NULL;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
struct binder_transaction *in_reply_to = NULL;
uint32_t return_error = 0;
struct list_head sgc_head;
struct list_head pf_head;
const void __user *user_buffer =
(const void __user *)(uintptr_t)
tr->data.ptr.buffer;
INIT_LIST_HEAD(&sgc_head);
INIT_LIST_HEAD(&pf_head);
/* 记录 transaction log,随后初始化 t */
}t 是内核事务 owner,t->buffer 将属于目标 proc 的 allocator;tcomplete 是发送方收到的完成 work。sgc_head 保存 scatter-gather 延迟复制,pf_head 保存父对象 fixup。它们必须在错误标签中一并释放,不能只释放 t。
源码文件:kernel/common/drivers/android/binder_internal.h
相关结构:struct binder_transaction
struct binder_transaction {
int debug_id;
struct binder_work work;
struct binder_thread *from;
struct binder_proc *to_proc;
struct binder_thread *to_thread;
struct binder_transaction *from_parent;
struct binder_transaction *to_parent;
unsigned is_async:1;
unsigned is_reply:1;
struct binder_buffer *buffer;
unsigned int code;
unsigned int flags;
struct binder_priority priority;
kuid_t sender_euid;
struct list_head fd_fixups;
spinlock_t lock;
};同步请求使用 from 和 from_parent 维护回复链;目标接收后通过 to_thread、to_parent 维护接收栈。oneway 没有发送方等待 reply 的 from 链,reply 事务则通过 in_reply_to 找回原请求。
3. 目标解析
3.1 Reply路径
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()、binder_get_txn_from_and_acq_inner()
if (reply) {
binder_inner_proc_lock(proc);
in_reply_to = thread->transaction_stack;
if (!in_reply_to) {
binder_inner_proc_unlock(proc);
return_error = BR_FAILED_REPLY;
return_error_param = -EPROTO;
goto err_empty_call_stack;
}
if (in_reply_to->to_thread != thread) {
binder_inner_proc_unlock(proc);
return_error = BR_FAILED_REPLY;
return_error_param = -EPROTO;
goto err_bad_call_stack;
}
thread->transaction_stack =
in_reply_to->to_parent;
binder_inner_proc_unlock(proc);
target_thread =
binder_get_txn_from_and_acq_inner(
in_reply_to);
if (!target_thread) {
return_error = BR_DEAD_REPLY;
goto err_dead_binder;
}
target_proc = target_thread->proc;
target_proc->tmp_ref++;
binder_inner_proc_unlock(target_proc);
}reply 不是根据 tr->target.handle 查找目标。当前线程必须有 transaction stack,且栈顶事务的 to_thread 必须正是当前 thread;检查通过后弹出当前栈项,再从原请求的 from 找回发送线程。发送线程死亡返回 BR_DEAD_REPLY,栈关系错误返回 BR_FAILED_REPLY/-EPROTO。
3.2 Handle路径
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()、binder_get_node_refs_for_txn()
if (!reply && tr->target.handle) {
struct binder_ref *ref;
binder_proc_lock(proc);
ref = binder_get_ref_olocked(
proc, tr->target.handle, true);
if (ref) {
target_node =
binder_get_node_refs_for_txn(
ref->node, &target_proc,
&return_error);
} else {
binder_user_error(
"%d:%d invalid handle\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
}
binder_proc_unlock(proc);
}handle 只属于发送方 proc。驱动先在发送方的 ref 红黑树找到 binder_ref,再从 ref 指向 node;binder_get_node_refs_for_txn() 同时增加 node 临时引用和目标 proc 临时引用,保证构造事务期间目标不会被 release。
3.3 Context路径
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()
if (!reply && !tr->target.handle) {
mutex_lock(
&context->context_mgr_node_lock);
target_node =
context->binder_context_mgr_node;
if (target_node) {
target_node =
binder_get_node_refs_for_txn(
target_node, &target_proc,
&return_error);
} else {
return_error = BR_DEAD_REPLY;
}
mutex_unlock(
&context->context_mgr_node_lock);
}handle 0 是 context manager 的特殊入口。若目标不存在,返回 BR_DEAD_REPLY;若当前 proc 就是 context manager 所属 proc,驱动拒绝 self transaction。随后还要执行 security_binder_transaction(proc->cred, target_proc->cred),权限失败返回 BR_FAILED_REPLY/-EPERM。
3.4 同步嵌套
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()
if (!reply && !(tr->flags & TF_ONE_WAY) &&
thread->transaction_stack) {
struct binder_transaction *tmp =
thread->transaction_stack;
while (tmp) {
struct binder_thread *from;
spin_lock(&tmp->lock);
from = tmp->from;
if (from && from->proc == target_proc) {
atomic_inc(&from->tmp_ref);
target_thread = from;
is_nested = true;
spin_unlock(&tmp->lock);
break;
}
spin_unlock(&tmp->lock);
tmp = tmp->from_parent;
}
}同步嵌套调用可能直接定位到调用栈上关联的发送线程,而不是总是从目标 proc 的 waiting_threads 选择线程。is_nested 会进入后续 priority 和栈处理,不能把所有同步事务都当成普通 proc work。
4. 初始化与优先级
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()
t = kzalloc(sizeof(*t), GFP_KERNEL);
if (!t) {
return_error = BR_FAILED_REPLY;
return_error_param = -ENOMEM;
goto err_alloc_t_failed;
}
INIT_LIST_HEAD(&t->fd_fixups);
spin_lock_init(&t->lock);
binder_stats_created(
BINDER_STAT_TRANSACTION);
t->debug_id = t_debug_id;
t->from_pid = proc->pid;
t->from_tid = thread->pid;
t->sender_euid = task_euid(proc->tsk);
t->code = tr->code;
t->flags = tr->flags;
t->work.type = BINDER_WORK_TRANSACTION;
t->is_async = !reply &&
(tr->flags & TF_ONE_WAY);
t->is_reply = reply;
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;同步请求设置 t->from,oneway 和 reply 不设置。发送方 UID 来自打开 proc 记录的 group leader task,而不是用户在 descriptor 中可任意填写的字段。
tcomplete = kzalloc(
sizeof(*tcomplete), GFP_KERNEL);
if (!tcomplete) {
return_error = BR_FAILED_REPLY;
return_error_param = -ENOMEM;
goto err_alloc_tcomplete_failed;
}
binder_stats_created(
BINDER_STAT_TRANSACTION_COMPLETE);
if (!reply && !(t->flags & TF_ONE_WAY) &&
binder_supported_policy(current->policy)) {
t->priority.sched_policy =
current->policy;
t->priority.prio = current->prio;
} else {
t->priority = target_proc->default_priority;
}同步事务继承支持的发送线程调度策略;oneway 或不支持的策略使用目标 proc 默认优先级。tcomplete 的创建和 priority 选择都发生在 buffer 分配前,失败时要沿对应标签释放。
5. 目标Buffer
5.1 安全上下文
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()
if (target_node &&
target_node->txn_security_ctx) {
ret = security_secid_to_secctx(
secid, &lsmctx);
if (ret < 0) {
return_error = BR_FAILED_REPLY;
return_error_param = ret;
goto err_get_secctx_failed;
}
added_size = ALIGN(
lsmctx.len, sizeof(u64));
extra_buffers_size += added_size;
if (extra_buffers_size < added_size) {
return_error = BR_FAILED_REPLY;
return_error_param = -EINVAL;
goto err_bad_extra_size;
}
}目标 node 请求 security context 时,驱动把发送方 cred 转成字符串并把对齐后的长度加入 extra buffer。整数溢出和 secctx 获取失败在 allocator 之前终止;security context 的释放也属于失败清理的一部分。
5.2 Allocator申请
源码文件:kernel/common/drivers/android/binder.c、kernel/common/drivers/android/binder_alloc.c
相关函数:binder_alloc_new_buf()
t->buffer = binder_alloc_new_buf(
&target_proc->alloc,
tr->data_size,
tr->offsets_size,
extra_buffers_size,
!reply && (t->flags & TF_ONE_WAY));
if (IS_ERR(t->buffer)) {
ret = PTR_ERR(t->buffer);
return_error_param = ret;
return_error = ret == -ESRCH
? BR_DEAD_REPLY
: BR_FAILED_REPLY;
t->buffer = NULL;
goto err_binder_alloc_buf_failed;
}binder_alloc_new_buf() 先确认目标 allocator mapped,再对齐 data/offsets/extra 大小,从 free tree 取区间并按需安装页。-ESRCH 表示目标 VMA 已清除或目标正在死亡,-ENOSPC 表示地址空间/async 空间不足,-ENOMEM 表示分配失败;它们最终转换成发送方可见的 BR 错误语义。
5.3 Buffer owner
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()
t->buffer->debug_id = t->debug_id;
t->buffer->transaction = t;
t->buffer->target_node = target_node;
t->buffer->clear_on_free =
!!(t->flags & TF_CLEAR_BUF);
if (lsmctx.context) {
size_t offset =
ALIGN(tr->data_size, sizeof(void *)) +
ALIGN(tr->offsets_size, sizeof(void *)) +
ALIGN(extra_buffers_size, sizeof(void *)) -
ALIGN(lsmctx.len, sizeof(u64));
t->security_ctx =
t->buffer->user_data + offset;
binder_alloc_copy_to_buffer(
&target_proc->alloc, t->buffer,
offset, lsmctx.context, lsmctx.len);
}buffer 属于 target_proc 的用户可见地址空间,但 transaction 对象仍由驱动持有。buffer->transaction 防止 allocator 在事务完成前释放它;buffer->target_node 决定读端产生 BR_TRANSACTION 还是 BR_REPLY。
6. 数据与对象
6.1 Offsets复制
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()、binder_alloc_copy_user_to_buffer()
if (binder_alloc_copy_user_to_buffer(
&target_proc->alloc, t->buffer,
ALIGN(tr->data_size, sizeof(void *)),
(const void __user *)(uintptr_t)
tr->data.ptr.offsets,
tr->offsets_size)) {
return_error = BR_FAILED_REPLY;
return_error_param = -EFAULT;
goto err_copy_data_failed;
}
if (!IS_ALIGNED(tr->offsets_size,
sizeof(binder_size_t))) {
return_error = BR_FAILED_REPLY;
return_error_param = -EINVAL;
goto err_bad_offset;
}offsets 区先进入目标 buffer,随后驱动逐个读取 offset,检查单调递增和对象大小,再复制对象前后的普通 payload。offsets 不是直接暴露给接收方的独立内核数组。
6.2 对象循环
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()
for (buffer_offset = off_start_offset;
buffer_offset < off_end_offset;
buffer_offset += sizeof(binder_size_t)) {
struct binder_object object;
binder_size_t object_offset;
size_t object_size;
binder_alloc_copy_from_buffer(
&target_proc->alloc, &object_offset,
t->buffer, buffer_offset,
sizeof(object_offset));
if (object_offset < off_min ||
object_offset > tr->data_size)
goto err_bad_offset;
object_size = binder_get_object(
target_proc, user_buffer, t->buffer,
object_offset, &object);
if (!object_size || object_offset < off_min)
goto err_bad_offset;
user_offset = object_offset + object_size;
switch (object.hdr.type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER:
ret = binder_translate_binder(
&object.fbo, t, thread);
break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE:
ret = binder_translate_handle(
&object.fbo, t, thread);
break;
case BINDER_TYPE_FD:
ret = binder_translate_fd(
object.fdo.fd, object_offset,
t, thread, in_reply_to);
break;
default:
ret = -EINVAL;
break;
}
if (ret < 0)
goto err_translate_failed;
}当前循环不是只检查 offset。它把本地 binder、远端 handle、fd 等对象转换到 target proc 的语义,并把修正后的对象写回目标 buffer;任一种 object 校验或翻译失败都会进入统一回滚。
6.3 Pointer与FDA
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()、binder_defer_copy()、binder_fixup_parent()
case BINDER_TYPE_PTR: {
struct binder_buffer_object *bp =
to_binder_buffer_object(&object.hdr);
if (bp->length >
sg_buf_end_offset - sg_buf_offset)
goto err_bad_offset;
ret = binder_defer_copy(
&sgc_head, sg_buf_offset,
(const void __user *)(uintptr_t)
bp->buffer, bp->length);
if (ret)
goto err_translate_failed;
bp->buffer =
t->buffer->user_data + sg_buf_offset;
sg_buf_offset += ALIGN(
bp->length, sizeof(u64));
ret = binder_fixup_parent(
&pf_head, t, thread, bp,
off_start_offset, num_valid,
last_fixup_obj_off,
last_fixup_min_off);
if (ret < 0)
goto err_translate_failed;
}BINDER_TYPE_PTR 的外部数据先登记为 deferred copy,接收 buffer object 的 pointer 再改成目标地址;FDA 还要验证 parent object、parent offset 的顺序,fd array 的 fixup 不能越过前一个父对象。
6.4 尾部复制
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()
if (binder_alloc_copy_user_to_buffer(
&target_proc->alloc, t->buffer,
user_offset,
user_buffer + user_offset,
tr->data_size - user_offset)) {
return_error = BR_FAILED_REPLY;
return_error_param = -EFAULT;
goto err_copy_data_failed;
}
ret = binder_do_deferred_txn_copies(
&target_proc->alloc, t->buffer,
&sgc_head, &pf_head);
if (ret) {
return_error = BR_FAILED_REPLY;
return_error_param = ret;
goto err_copy_data_failed;
}对象循环只在对象边界之间复制普通数据,完成对象修复后才复制尾部并执行 deferred copies。这样普通 payload、objects、外部 pointer data 和 fd fixup 的 owner 不会混成一个字节拷贝。
7. 入队唤醒
7.1 Reply投递
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()
if (reply) {
binder_enqueue_thread_work(
thread, tcomplete);
binder_inner_proc_lock(target_proc);
if (target_thread->is_dead) {
binder_inner_proc_unlock(target_proc);
return_error = BR_DEAD_REPLY;
goto err_dead_proc_or_thread;
}
binder_pop_transaction_ilocked(
target_thread, in_reply_to);
binder_enqueue_thread_work_ilocked(
target_thread, &t->work);
target_proc->outstanding_txns++;
binder_inner_proc_unlock(target_proc);
wake_up_interruptible_sync(
&target_thread->wait);
binder_restore_priority(
thread, &in_reply_to->saved_priority);
binder_free_transaction(in_reply_to);
}reply 的目标是原同步调用线程。驱动先给当前回复线程排入 transaction complete,再从原发送线程 的 stack 弹出 in_reply_to,把新的 reply transaction work 放入该线程 todo 并同步唤醒。原请求 事务随后释放;如果发送线程已经死亡,reply 转入 dead-reply 清理。
7.2 同步请求
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()、binder_proc_transaction()
else if (!(t->flags & TF_ONE_WAY)) {
binder_enqueue_deferred_thread_work_ilocked(
thread, tcomplete);
t->from_parent =
thread->transaction_stack;
thread->transaction_stack = t;
return_error = binder_proc_transaction(
t, target_proc, target_thread);
if (return_error) {
binder_pop_transaction_ilocked(
thread, t);
goto err_dead_proc_or_thread;
}
}同步请求先把发送方 transaction 放入自己的 stack,再交给目标 proc。binder_proc_transaction() 如果指定了 target_thread 就入该线程 todo,否则从 waiting_threads 选择;没有可用线程就进 proc todo。失败时必须把发送方 stack 弹回。这里的 else if 表明 reply 已由上一分支处理,不能再次 当成普通同步请求排队。
7.3 Oneway事务
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()、binder_proc_transaction()
if (t->flags & TF_ONE_WAY) {
BUG_ON(target_node == NULL);
return_error = binder_proc_transaction(
t, target_proc, NULL);
if (return_error ==
BR_TRANSACTION_PENDING_FROZEN) {
tcomplete->type =
BINDER_WORK_TRANSACTION_PENDING;
}
binder_enqueue_thread_work(
thread, tcomplete);
if (return_error &&
return_error !=
BR_TRANSACTION_PENDING_FROZEN)
goto err_dead_proc_or_thread;
}oneway 不预先指定目标线程。某 node 没有活动异步事务时,第一笔 oneway 仍可选择 waiting thread 或进入 proc todo;node->has_async_transaction 已为 true 时,后续事务才进入 node->async_todo 串行等待。目标冻结时 oneway 可以进入 pending 队列并返回 BR_TRANSACTION_PENDING_FROZEN;同步事务则返回 BR_FROZEN_REPLY。
7.4 目标选择
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_proc_transaction()
if (proc->is_frozen) {
frozen = true;
proc->sync_recv |= !oneway;
proc->async_recv |= oneway;
}
if ((frozen && !oneway) ||
proc->is_dead ||
(thread && thread->is_dead))
return frozen ?
BR_FROZEN_REPLY : BR_DEAD_REPLY;
if (!thread && !pending_async)
thread = binder_select_thread_ilocked(proc);
if (thread)
binder_enqueue_thread_work_ilocked(
thread, &t->work);
else if (!pending_async)
binder_enqueue_work_ilocked(
&t->work, &proc->todo);
else
binder_enqueue_work_ilocked(
&t->work, &node->async_todo);
if (!pending_async)
binder_wakeup_thread_ilocked(
proc, thread, !oneway);目标 proc 的 frozen/dead 状态先于入队检查。同步 work 优先投递到指定或等待线程,普通 proc work 没有线程时留在 proc todo;oneway 已有活动 async work 时进入 node async_todo。pending async 不会立即唤醒线程,只有当前异步事务释放 buffer 时才把下一项移到 proc todo 并唤醒;所有成功 入队的事务都会增加 proc outstanding_txns。
8. 完成与释放
8.1 成功收束
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()
if (target_thread)
binder_thread_dec_tmpref(
target_thread);
binder_proc_dec_tmpref(target_proc);
if (target_node)
binder_dec_node_tmpref(target_node);
smp_wmb();
WRITE_ONCE(e->debug_id_done,
t_debug_id);
return;成功后释放目标 thread/proc/node 的临时引用;transaction 本身继续由目标 work、transaction stack 或 buffer 生命周期持有。发送方的 tcomplete 会在目标回复或事务完成路径中变成 BR_TRANSACTION_COMPLETE。
8.2 失败收束
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction() 错误标签
err_translate_failed:
err_bad_offset:
err_copy_data_failed:
binder_cleanup_deferred_txn_lists(
&sgc_head, &pf_head);
binder_free_txn_fixups(t);
binder_transaction_buffer_release(
target_proc, NULL, t->buffer,
buffer_offset, true);
if (target_node)
binder_dec_node_tmpref(
target_node);
t->buffer->transaction = NULL;
binder_alloc_free_buf(
&target_proc->alloc, t->buffer);
kfree(tcomplete);
binder_stats_deleted(
BINDER_STAT_TRANSACTION_COMPLETE);
err_dead_binder:
err_invalid_target_handle:
if (target_node) {
binder_dec_node(target_node, 1, 0);
binder_dec_node_tmpref(target_node);
}
binder_netlink_report(
proc, t, tr->data_size, return_error);
kfree(t);失败路径按已建立资源分层清理:deferred copies、fd fixups、buffer 内容、allocator buffer、tcomplete、node/proc/thread 临时引用各有对应释放。return_error 进入发送线程的 error/reply work,用户态最终看到 BR 错误或扩展错误,而不是一个“静默丢弃”。
8.3 事务状态
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_transaction()、binder_set_txn_from_error()
if (from->ee.command == BR_OK)
binder_set_extended_error(
&from->ee, id, command, param);驱动只在发送线程的 extended error 为空时写入错误,避免后续失败覆盖第一根因。事务失败日志还记录 debug id、方向、目标 PID/TID、命令码、数据大小和错误行,适合与 BINDER_GET_EXTENDED_ERROR 对照。
9. 测试输入
9.1 空事务
源码文件:frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp
相关测试:Transaction
struct {
uint32_t cmd;
binder_transaction_data tr;
} __attribute__((packed)) bc = {
.cmd = BC_TRANSACTION,
.tr = {
.target = {0},
.code = android::IBinder::PING_TRANSACTION,
.flags = 0,
.data_size = 0,
.offsets_size = 0,
},
};
bwr.write_buffer = (uintptr_t)&bc;
bwr.write_size = sizeof(bc);
bwr.read_buffer = (uintptr_t)&br;
bwr.read_size = sizeof(br);
binderTestIoctlSuccessOrError(
BINDER_WRITE_READ, &bwr, EAGAIN);测试输入覆盖 BC descriptor、空 data/offsets 和 read buffer。后续断言检查 BR_NOOP、BR_TRANSACTION_COMPLETE、BR_REPLY 及 reply descriptor 的零字段,证明入口和基本 reply 形状,不证明非空对象翻译或真实服务业务。
9.2 Oneway
源码文件:frameworks/native/libs/binder/tests/binderLibTest.cpp
相关测试:NopTransactionOneway、OnewayQueueing
TEST_F(BinderLibTest, NopTransactionOneway) {
Parcel data, reply;
EXPECT_THAT(
m_server->transact(
BINDER_LIB_TEST_NOP_TRANSACTION,
data, &reply, TF_ONE_WAY),
StatusEq(NO_ERROR));
}测试断言用户态 oneway 调用返回成功,但服务处理和回调时序由后续 queueing 测试验证。OnewayQueueing 使用一个延迟 callback 和一个立即 callback,检查同一 node 的 async queue 顺序;它不证明所有多线程服务都串行。
9.3 冻结
源码文件:frameworks/native/libs/binder/tests/binderLibTest.cpp
相关测试:FreezeTxn
EXPECT_EQ(NO_ERROR,
IPCThreadState::self()->freeze(
pid, true, 1000));
EXPECT_EQ(frozenError(),
server->transact(
BINDER_LIB_TEST_NOP_TRANSACTION,
data, &reply));
EXPECT_EQ(NO_ERROR,
IPCThreadState::self()->freeze(
pid, false, 0));
EXPECT_EQ(NO_ERROR,
server->transact(
BINDER_LIB_TEST_NOP_TRANSACTION,
data, &reply));冻结测试输入是目标 PID、冻结开关和超时;断言冻结期间同步事务返回 frozen error,解冻后同一调用恢复成功。它证明 binder_proc_transaction() 的 frozen 分支可见于用户态,不覆盖设备权限和所有 pending oneway 变体。
10. 失败边界
| 阶段 | 失败 | 结果 |
|---|---|---|
| reply 栈 | 无栈或栈目标错误 | BR_FAILED_REPLY/-EPROTO |
| 目标 ref/node | handle 无效或 node 死亡 | BR_FAILED_REPLY 或 BR_DEAD_REPLY |
| 安全检查 | cred 不允许 | BR_FAILED_REPLY/-EPERM |
| allocator | VMA 消失、空间不足、内存不足 | dead/failed reply |
| offsets/object | 越界、未对齐、类型或 parent 错误 | buffer 回滚、failed reply |
| 目标状态 | frozen/dead thread/proc | frozen/dead reply |
| 入队后 | target work 消费前进程退出 | cleanup transaction、dead reply |
事务函数中的 BR 错误是发送方协议可见结果;外层 BINDER_WRITE_READ 还可能因为用户 buffer 回写失败返回 errno。两层错误必须分开定位。
11. 源码复现
事务阶段
图中 Allocate、Copy、Fixup 是不同失败点;它们最终可能都映射成客户端失败,但清理对象和下一步 诊断证据不同。事务到达 Queue 后才有机会被服务线程消费。
本文主线:
BC_TRANSACTION/BC_REPLY
→ binder_thread_write
→ binder_transaction
→ reply栈或 handle/context node
→ target proc/thread + refs
→ transaction/tcomplete
→ allocator buffer与按需页
→ payload/object/fd/pointer翻译
→ binder_proc_transaction
→ thread/proc/node async work
→ 唤醒与 BR 结果
→ 引用、buffer、事务清理源码搜索:
rg -n "binder_transaction\(|binder_proc_transaction\(" \
kernel/common/drivers/android/binder.c
rg -n "binder_get_node_refs_for_txn|binder_get_txn_from_and_acq_inner" \
kernel/common/drivers/android/binder.c
rg -n "binder_alloc_new_buf|binder_translate_|binder_fixup_parent|binder_do_deferred_txn_copies" \
kernel/common/drivers/android/binder.c \
kernel/common/drivers/android/binder_alloc.c
rg -n "NopTransactionOneway|OnewayQueueing|FreezeTxn|TEST_F.*Transaction" \
frameworks/native/libs/binder/tests/binderLibTest.cpp \
frameworks/native/libs/binder/tests/binderDriverInterfaceTest.cpp如果能够解释 reply 为什么从 transaction stack 找目标、为什么 handle 需要 node/proc 临时引用、为什么 object 翻译失败必须释放目标 buffer,以及为什么 oneway work 进入 node async_todo 而不是直接指定线程,就已经建立了 binder_transaction() 的可继续阅读模型。
