3. 线程池参数全景:最大线程数、最小线程数、超时时间、空闲线程回收策略、优先级继承规则

Binder 线程池的参数决定了服务端处理事务的并行度、响应延迟和资源消耗。在 AOSP 原生实现中,线程池由 IPCThreadState 和 ProcessState 协同管理,核心参数分散在内核驱动层(/dev/binder)与用户态 Binder 库中。本章逐一拆解五大参数的作用域、默认值、调优影响及配置方法。

3.1 参数全景与默认值

参数默认值作用域影响维度
最大线程数 (max_threads)15 / BINDER_IOCTL每进程 Binder 上下文并发上限、内存占用
最小线程数 (min_threads)1用户态 ProcessState启动预热、空闲功耗
超时时间 (timeout)300 s (talk_man_sec)Binder 驱动死锁检测、卡顿恢复
空闲线程回收策略LFU + 延迟回收IPCThreadState内存碎片、抖动
优先级继承规则BINDER_IPI -> SCHED_FIFO内核调度器实时性、优先级反转

3.2 最大线程数:并发上限压测维度

原理:驱动通过 BINDER_SET_MAX_THREADS ioctl 限制每个 binder_proc 可创建的内核工作线程数目。超过上限时,同步事务进入等待队列;异步事务被直接拒绝(返回 BR_DEAD_REPLY)。

// frameworks/native/libs/binder/ProcessState.cpp
status_t ProcessState::setThreadCount(int32_t maxThreads) {
    if (maxThreads < 1) return BAD_VALUE;
    mMaxThreads = maxThreads;
    // 通过 ioctl 通知驱动
    ioctl(mDriverFD, BINDER_SET_MAX_THREADS, &maxThreads);
    return NO_ERROR;
}
调优经验:音频或相机服务建议提升到 32~64,但每增加一个线程约占用 1.5 MB 内核栈 + 2 MB 用户态栈。内存敏感场景(如 system_server)建议保持默认 15 并通过优先级分流降低峰值并发。

3.3 最小线程数:预热与空闲功耗

原理:ProcessState::self()->startThreadPool() 启动的线程数量即为最小线程。若未主动调用,依赖 onFirstRef 的隐式线程。最小线程不足时,首个事务需触发线程创建,增加延迟 200~800 μs。

// 修改默认最小线程数(需在 service 启动前调用)
ProcessState::self()->setThreadCount(4);   // 最大
ProcessState::self()->setMinThreadCount(2); // 最小,非标准 API,需自行扩展
注意:AOSP 未开放 setMinThreadCount,最小线程数实为 startThreadPool() 的启动线程数。若需要动态保持,需通过定时器创建线程并循环执行 joinThreadPool() 防止回收。

3.4 超时时间:死锁与恢复

原理:Binder 驱动通过 binder_proc.talk_man_sec 记录事务最长等待时间。超时后,驱动主动返回 BR_TIMEOUT,用户态收到 FAILED_TRANSACTION(errno 为 ETIMEDOUT)。

// 内核驱动:drivers/staging/android/binder.c
static void binder_wakeup_poll_threads_ilocked(struct binder_proc *proc) {
    // 检查 talk_man_sec,超过阈值强制唤醒
    if (proc->talk_man_sec > proc->default_talk_man_sec) {
        wake_up_interruptible(&proc->wait);
    }
}

调试命令:

# 查看当前进程超时配置
adb shell cat /d/binder/proc/*/stats | grep "talk_man_sec"
# 设置全局超时(需 root)
adb shell "echo 120 > /sys/module/binder/parameters/talk_man_sec"

3.5 空闲线程回收策略:防止抖动

策略:当线程空闲超过 mWaitTime(默认 30 s),IPCThreadState::joinThreadPool() 退出循环,线程自然销毁。不采用 LRU,而采用“最后唤醒次数最少”的 LFU 近似。回收时预留 MIN_THREADS 保证突发请求。

回收触发条件(任一满足):
  • 线程执行完一个事务后,空闲时间 > 30 s
  • 当前活跃线程数远小于最大线程数且连续 10 次 poll 无事务
  • 系统内存压力回调 (kernel 通过 shrinker 通知)

3.6 优先级继承:实时性与反转防范

规则链:当高优先级线程(如 RT)向低优先级服务发起 Binder 调用时,驱动内核自动将目标线程的优先级提升至发起方级别(PI,Priority Inheritance)。提权作用域仅限于本次事务执行期间,事务结束后立即恢复。

// 内核实现:binder_thread 结构体包含 priority 字段
struct binder_thread {
    struct binder_proc *proc;
    int priority;          // 当前有效优先级
    int prio_raised;       // 是否因继承而提权
    // ...
};

可观察现象:通过 schedtop 或 trace sched_switch 可以看到 Binder 线程的优先级动态变化。若出现优先级反转死锁,需检查是否缺少 PI 支持(某些自定义内核可能未开启 CONFIG_BINDER_PRIORITY_INHERITANCE)。

常见错误:优先继承只在同步事务(BC_TRANSACTION / BC_REPLY)中生效。若服务使用 oneway 异步调用,不会触发 PI,高优先级线程可能被低优先级服务持续占有 CPU。

3.7 参数调优原则

场景最大线程数最小线程数超时时间回收延迟优先级继承
高频低负载(输入事件)8~152~460 s10 s开启
高吞吐批处理(媒体 codec)32~644~8300 s60 s开启
实时响应(Sensor HAL)4~8230 s5 s开启 + 固定 RT 队列
内存受限(zygote 子进程)3~51120 s30 s异步 oneway 规避
核心调优流程:
  1. 通过 adb shell cat /d/binder/state 获取当前线程数、等待队列深度
  2. 观察并发峰值:BINDER_TRANSACTION_COUNT + BINDER_THREAD_COUNT
  3. 调整最大线程数并观测分配/回收频率
  4. 若大量 BR_TIMEOUT 日志,调大超时时间 50~100%
  5. 若出现优先级反转,检查是否误用 oneway 接口,改为同步事务