Binder优先级继承
优先级继承不是发送方直接修改目标线程的调度策略,而是驱动在事务交付前依据 transaction flags、node policy、调用者实时属性和目标限制计算一次目标优先级,并在事务完成或嵌套返回时恢复。下图对应 binder_transaction()、binder_do_set_priority() 和保存/恢复字段。
本文承接 Binder线程池创建 和 Binder线程上限。Binder 优先级继承不是简单地把调用方 nice 复制给服务线程:事务先携带调度策略,内核再结合目标 Binder 节点的最低策略、inherit_rt、CAP_SYS_NICE 和 rlimit 决定实际值;同步调用返回后还要恢复服务线程原来的优先级。oneway 事务则走默认优先级路径。
1. 事务优先级
源码文件:kernel/common/drivers/android/binder.c
if (!(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 没有等待 reply,直接使用目标进程默认优先级;不受支持的调用方策略也回退到目标默认值。
2. 节点约束
2.1 Java/Native对象
源码文件:frameworks/native/libs/binder/Binder.cpp
BBinder::setMinSchedulerPolicy 保存节点的最低调度策略和优先级;setInheritRt 保存是否允许实时策略继承。这两个设置必须在 Binder 对象被 parcel/send 之前完成,源码在对象已发送后会 fatal。
2.2 Parcel标记
源码文件:frameworks/native/libs/binder/Parcel.cpp
flatten 本地 Binder 时,把显式策略编码进 FLAT_BINDER_FLAG_PRIORITY_MASK/FLAT_BINDER_FLAG_SCHED_POLICY_MASK,把实时继承编码进 FLAT_BINDER_FLAG_INHERIT_RT。因此服务节点约束随 Binder 节点进入内核,而不是每次业务调用重新协商。
3. 目标线程提权
源码文件:kernel/common/drivers/android/binder.c
static void binder_transaction_priority(struct binder_thread *thread,
struct binder_transaction *t, struct binder_node *node) {
struct binder_priority desired = t->priority;
const struct binder_priority node_prio = {
.sched_policy = node->sched_policy,
.prio = node->min_priority,
};
if (!node->inherit_rt && is_rt_policy(desired.sched_policy)) {
desired.prio = NICE_TO_PRIO(0);
desired.sched_policy = SCHED_NORMAL;
}
if (node_prio.prio < desired.prio ||
(node_prio.prio == desired.prio && node_prio.sched_policy == SCHED_FIFO)) {
desired = node_prio;
}
binder_set_priority(thread, &desired);
}服务线程最终采用调用方优先级和节点最低优先级中更高者。节点未允许 inherit_rt 时,调用方的实时策略先降为 SCHED_NORMAL;节点最低策略可以进一步把线程提升到服务要求的最低等级。
4. 内核限制
4.1 实时策略
源码文件:kernel/common/drivers/android/binder.c 的 binder_do_set_priority
若目标线程没有 CAP_SYS_NICE,实时策略受 RLIMIT_RTPRIO 限制;额度为 0 时回退为 SCHED_NORMAL 和 MIN_NICE,额度不足时裁剪优先级。Binder 继承请求不是绕过 Linux 调度权限的特权通道。
4.2 普通策略
对 fair policy,内核根据 RLIMIT_NICE 计算允许的最小 nice;请求低于权限边界时使用允许值。to_userspace_prio/to_kernel_prio 在 nice 与内核 priority 表示之间转换,日志会记录被裁剪的目标值。
5. 保存恢复
源码文件:kernel/common/drivers/android/binder.c
应用新事务前,内核保存服务线程当前 task->policy 和 task->normal_prio 到 t->saved_priority。同步 reply 到达时调用 binder_restore_priority;如果期间有嵌套事务设置了新的 pending priority,恢复会被标记为 abort,避免外层 reply 把嵌套调用刚设置的优先级覆盖掉。
6. 嵌套调用
thread->transaction_stack 记录同步事务嵌套关系。内核在嵌套事务进入时保存 pending 状态,在 reply 或错误路径沿对应 transaction 恢复。由此可见,优先级继承与同步调用栈绑定;不能只在一次 transact 的入口观察线程 nice 就推断整个调用链。
7. 测试边界
源码文件:frameworks/native/libs/binder/tests/binderLibTest.cpp
测试服务在发送前调用 setMinSchedulerPolicy 和 setInheritRt(true),验证节点级扩展机制;这两个设置只对 local Binder 合法,NDK 测试还断言对 remote Binder 调用 AIBinder_setInheritRt 会终止。
源码文件:frameworks/native/libs/binder/tests/schd-dbg.cpp
调度调试程序以成对 client/server 运行多轮事务,退出码统计未发生优先级继承的事务数,最终输出 inheritance: PASS/FAIL。它验证的是实际调度结果,而不是仅检查事务结构体字段。
8. 失败边界
- oneway 调用不会继承调用方实时策略,而是使用目标进程默认值。
- 服务对象已经发送后再设置最低策略或
inherit_rt会 fatal。 - 没有
CAP_SYS_NICE时,继承可能被 rlimit 裁剪或降级。 - 嵌套事务可能使一次旧的 restore 被 abort,不能把“reply 后恢复”理解为无条件恢复到最初 nice。
9. 阅读检查
给定一个 SCHED_FIFO 调用者和普通服务节点,沿事务创建、Parcel 节点 flags、binder_transaction_priority、权限裁剪、reply restore 五处源码判断服务线程最终策略。再把 inherit_rt=true 和 oneway 两种条件代入,解释结果为何不同。
