Binder线程状态模型
Binder 线程池不是“固定 N 个线程等待请求”。当前源码允许 ProcessState 启动一个主池线程, 驱动按需请求更多线程,调用者直接 joinThreadPool,或使用 setupPolling 把 Binder fd 接入自己的 epoll。驱动还要区分空闲线程、正在处理嵌套事务的线程和只能消费 thread.todo 的线程。
本文面向已经读过 Binder协议状态机 和 Binder的C/S模型 的读者,专门回答线程从哪里创建、如何注册、何时 进入 waiting_threads、事务如何选线程、BR_SPAWN_LOOPER 如何闭环、嵌套同步调用为何可能回到 原线程,以及退出和饥饿如何观测。
本文不把默认 maxThreads 当成设备通用线程总数,也不展开 Java Binder 主线程、system_server 每个 ServiceThread 或完整优先级继承算法。
1. 四种入口
PoolThread
源码文件:frameworks/native/libs/binder/ProcessState.cpp
class PoolThread : public Thread {
public:
explicit PoolThread(bool isMain)
: mIsMain(isMain) {}
protected:
bool threadLoop() override {
IPCThreadState::self()
->joinThreadPool(mIsMain);
return false;
}
const bool mIsMain;
};PoolThread 只是线程载体;真正的 Binder 循环在当前线程的 IPCThreadState 中。一个 ProcessState 对应进程级驱动 fd,但每个线程拥有独立 TLS IPCThreadState 和 mIn/mOut。
入口对照
| 入口 | 注册命令 | owner | 典型用途 |
|---|---|---|---|
| startThreadPool | 主 PoolThread 发送 BC_ENTER_LOOPER | ProcessState | 标准线程池 |
| BR_SPAWN_LOOPER | 新 PoolThread 发送 BC_REGISTER_LOOPER | 驱动请求/ProcessState 创建 | 动态扩容 |
| 直接 joinThreadPool | 参数决定 ENTER/REGISTER | 调用线程 | 自定义服务主线程 |
| setupPolling | BC_ENTER_LOOPER | 外部 event loop | epoll 集成 |
线程池总量不能简单写成 maxThreads+1,因为直接 join 和 polling 线程也可能存在。
2. TLS状态
self入口
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
IPCThreadState* IPCThreadState::self() {
if (gHaveTLS.load(
std::memory_order_acquire)) {
restart:
const pthread_key_t key = gTLS;
IPCThreadState* state =
static_cast<IPCThreadState*>(
pthread_getspecific(key));
if (state)
return state;
return new IPCThreadState;
}
pthread_mutex_lock(&gTLSMutex);
if (!gHaveTLS.load(
std::memory_order_relaxed)) {
int result = pthread_key_create(
&gTLS, threadDestructor);
if (result != 0) {
pthread_mutex_unlock(&gTLSMutex);
return nullptr;
}
gHaveTLS.store(
true,
std::memory_order_release);
}
pthread_mutex_unlock(&gTLSMutex);
goto restart;
}TLS key 是进程共享的,但 value 属于线程。一个线程的 reply wait、calling identity、pending deref 和事务输入不能被另一个线程直接替代。
Thread状态
驱动中的 binder_thread 保存 pid、looper bits、transaction_stack、thread todo、wait queue 和 错误状态。用户态 IPCThreadState 与内核 binder_thread 通过“当前调用线程 + 同一个驱动 fd” 对应,不是通过 Java Thread 对象引用连接。
3. 启动线程池
startThreadPool
源码文件:frameworks/native/libs/binder/ProcessState.cpp
void ProcessState::startThreadPool() {
std::unique_lock<std::mutex> lock(mLock);
if (!mThreadPoolStarted) {
if (mMaxThreads == 0) {
ALOGW("Extra binder thread started, "
"but 0 threads requested.");
}
mThreadPoolStarted = true;
spawnPooledThread(true);
}
}启动是幂等的,首次调用创建 isMain=true 的 PoolThread。它不代表调用 startThreadPool 的原线程 加入池;新建的 PoolThread 才调用 joinThreadPool。
动态创建
源码文件:frameworks/native/libs/binder/ProcessState.cpp
void ProcessState::spawnPooledThread(
bool isMain) {
if (mThreadPoolStarted) {
String8 name =
makeBinderThreadName();
sp<Thread> thread =
sp<PoolThread>::make(isMain);
thread->run(name.c_str());
mKernelStartedThreads++;
}
}函数名中的 pooled 并不表示内核直接创建 pthread;驱动只返回 BR_SPAWN_LOOPER,libbinder 再 创建 PoolThread。
最大线程配置
源码文件:frameworks/native/libs/binder/ProcessState.cpp
status_t ProcessState::
setThreadPoolMaxThreadCount(
size_t maxThreads) {
LOG_ALWAYS_FATAL_IF(
mThreadPoolStarted &&
maxThreads < mMaxThreads,
"Binder threadpool cannot be shrunk "
"after starting");
if (ioctl(mDriverFD,
BINDER_SET_MAX_THREADS,
&maxThreads) != -1) {
mMaxThreads = maxThreads;
return NO_ERROR;
}
return -errno;
}线程池启动后不能缩小用户态配置。驱动 max_threads 约束的是驱动请求启动的线程,不自动统计 所有直接 join 或 polling 线程。
4. 注册状态
join循环
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
void IPCThreadState::joinThreadPool(
bool isMain) {
mProcess->checkExpectingThreadPoolStart();
mProcess->mCurrentThreads++;
mOut.writeInt32(
isMain
? BC_ENTER_LOOPER
: BC_REGISTER_LOOPER);
mIsLooper = true;
status_t result;
do {
processPendingDerefs();
result = getAndExecuteCommand();
if (result == TIMED_OUT &&
!isMain) {
break;
}
} while (result != -ECONNREFUSED &&
result != -EBADF);
mOut.writeInt32(BC_EXIT_LOOPER);
mIsLooper = false;
talkWithDriver(false);
mProcess->mCurrentThreads--;
}主 PoolThread 发送 ENTER,驱动请求创建的 PoolThread 发送 REGISTER。非主线程只有收到 TIMED_OUT 才可从循环退出;实际是否产生该返回取决于驱动命令和当前实现,不能写成固定空闲 超时。
驱动校验
源码文件:kernel/common/drivers/android/binder.c
case BC_REGISTER_LOOPER:
binder_inner_proc_lock(proc);
if (thread->looper &
BINDER_LOOPER_STATE_ENTERED) {
thread->looper |=
BINDER_LOOPER_STATE_INVALID;
} else if (proc->requested_threads == 0) {
thread->looper |=
BINDER_LOOPER_STATE_INVALID;
} else {
proc->requested_threads--;
proc->requested_threads_started++;
}
thread->looper |=
BINDER_LOOPER_STATE_REGISTERED;
binder_inner_proc_unlock(proc);
break;
case BC_ENTER_LOOPER:
if (thread->looper &
BINDER_LOOPER_STATE_REGISTERED) {
thread->looper |=
BINDER_LOOPER_STATE_INVALID;
}
thread->looper |=
BINDER_LOOPER_STATE_ENTERED;
break;REGISTER 必须对应驱动之前的 requested_threads;ENTER 和 REGISTER 不能在同一线程交叉使用。 协议错误会标记 INVALID,而不是把线程默认为正常 worker。
5. 空闲等待
可消费proc work
源码文件:kernel/common/drivers/android/binder.c
static bool
binder_available_for_proc_work_ilocked(
struct binder_thread *thread) {
return !thread->transaction_stack &&
binder_worklist_empty_ilocked(
&thread->todo);
}只有没有嵌套 transaction_stack 且 thread.todo 为空的线程,才可去消费 proc.todo。正在等待某个 reply 或拥有定向 work 的线程不是通用空闲线程。
waiting_threads
源码文件:kernel/common/drivers/android/binder.c
prepare_to_wait(&thread->wait,
&wait,
TASK_INTERRUPTIBLE |
TASK_FREEZABLE);
if (binder_has_work_ilocked(
thread, do_proc_work))
break;
if (do_proc_work)
list_add(
&thread->waiting_thread_node,
&proc->waiting_threads);
binder_inner_proc_unlock(proc);
schedule();
binder_inner_proc_lock(proc);
list_del_init(
&thread->waiting_thread_node);线程只有在允许 proc work 时才加入 waiting_threads。被选中后会从链表移除,选择者必须负责 唤醒;信号中断则返回 EINTR。
选择线程
源码文件:kernel/common/drivers/android/binder.c
static struct binder_thread*
binder_select_thread_ilocked(
struct binder_proc *proc) {
struct binder_thread* thread =
list_first_entry_or_null(
&proc->waiting_threads,
struct binder_thread,
waiting_thread_node);
if (thread)
list_del_init(
&thread->waiting_thread_node);
return thread;
}选择规则不是 round-robin 业务调度器,只取 waiting_threads 头部;没有 waiting thread 时, 事务进入 proc.todo,或唤醒使用 poll 接口的线程。
6. 事务投递
同步事务
源码文件:kernel/common/drivers/android/binder.c
相关函数:binder_proc_transaction()
if (!thread && !pending_async)
thread =
binder_select_thread_ilocked(proc);
if (thread) {
binder_transaction_priority(
thread, transaction, node);
binder_enqueue_thread_work_ilocked(
thread,
&transaction->work);
} else if (!pending_async) {
binder_enqueue_work_ilocked(
&transaction->work,
&proc->todo);
}有空闲线程时事务定向到 thread.todo;否则进入 proc.todo,等待某个可消费 proc work 的线程。 同步事务还可能因 transaction_stack 找到嵌套调用关联线程,这与普通 waiting_threads 选择不同。
Oneway事务
同一 node 已有 async transaction 时,新 oneway 进入 node.async_todo;它不通过增加线程数获得 并行消费。oneway 顺序、同步线程池容量和 node async 队列是三个不同边界。
7. 动态扩容
生成条件
源码文件:kernel/common/drivers/android/binder.c
if (proc->requested_threads == 0 &&
list_empty(
&proc->waiting_threads) &&
proc->requested_threads_started <
proc->max_threads &&
(thread->looper &
(BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED))) {
proc->requested_threads++;
put_user(BR_SPAWN_LOOPER,
(uint32_t __user*)buffer);
}驱动只在没有未完成请求、没有 waiting thread、已启动数量低于 max,并且当前线程是合法 looper 时请求新线程。max_threads 不是当前总线程数,也不含所有用户直接 join 的线程。
用户态响应
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
case BR_SPAWN_LOOPER:
mProcess->spawnPooledThread(false);
break;新线程启动后发送 BC_REGISTER_LOOPER,驱动再把 requested_threads 减一并增加 requested_threads_started。只有请求和注册都完成,扩容才闭环。
8. 线程计数
总量估算
源码文件:frameworks/native/libs/binder/ProcessState.cpp
相关函数:getThreadPoolMaxTotalThreadCount()
size_t kernelStarted =
mKernelStartedThreads;
if (mThreadPoolStarted) {
size_t threads = 1;
threads += mMaxThreads;
size_t current = mCurrentThreads;
if (current > kernelStarted)
threads += current - kernelStarted;
return threads;
}
return mCurrentThreads;返回值是上界/估算:1 个 startThreadPool 线程、驱动可启动的 max,以及无法单独统计的直接 join 线程。源码注释明确存在竞态,因此不能把它当作某一时刻精确活跃数。
执行计数
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
size_t executing =
mProcess->mExecutingThreadsCount
.fetch_add(1) + 1;
if (executing >= mProcess->mMaxThreads) {
auto expected = ProcessState::never();
mProcess->mStarvationStartTime
.compare_exchange_strong(
expected,
std::chrono::steady_clock::now());
}
result = executeCommand(cmd);
executing =
mProcess->mExecutingThreadsCount
.fetch_sub(1) - 1;执行计数围绕 top-level command,而不是线程是否存在。达到 max 后记录 starvation 时间;这只是 诊断信号,不等于驱动必然能再创建线程。
9. Poll模式
setupPolling
源码文件:frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::setupPolling(
int* fd) {
if (mProcess->mDriverFD < 0)
return -EBADF;
mOut.writeInt32(BC_ENTER_LOOPER);
flushCommands();
*fd = mProcess->mDriverFD;
mProcess->mCurrentThreads++;
return NO_ERROR;
}外部循环拿 Binder fd 加入 epoll,事件到达后调用 handlePolledCommands。poll 线程不一定出现在 waiting_threads,因此驱动找不到普通 waiting thread 时会扫描并唤醒 poll looper。
Poll消费
do {
result = getAndExecuteCommand();
} while (mIn.dataPosition() <
mIn.dataSize());
processPendingDerefs();
flushCommands();poll consumer 必须把当前 mIn 命令处理完,并 flush BC_FREE_BUFFER 等后续命令。只读一个命令会 遗留协议状态。
10. 退出失败
Looper退出
BC_EXIT_LOOPER 标记内核 thread looper EXITED;驱动连接返回 ECONNREFUSED 或 EBADF 时,join 循环退出。非预期错误会 fatal,而不是静默继续。
线程死亡
服务线程死亡时,定向 transaction、transaction_stack、thread.todo 和 reply error 都需要驱动 清理;尚未回复的同步调用可能转为 BR_DEAD_REPLY。线程退出不等于整个服务 proc/node 死亡。
饥饿
所有 worker 阻塞在业务锁、同步回调或长事务时,proc.todo 会堆积。BR_SPAWN_LOOPER 受 max 和 requested 状态限制;增加 max 不能解决锁循环、oneway 单 node 串行或嵌套死锁。
11. 测试输入
线程总量
源码文件:frameworks/native/libs/binder/tests/binderLibTest.cpp
相关测试:ThreadPoolAvailableThreads()
EXPECT_THAT(server->transact(
BINDER_LIB_TEST_GET_MAX_THREAD_COUNT,
data, &reply), StatusEq(NO_ERROR));
int32_t count = reply.readInt32();
EXPECT_TRUE(count == kKernelThreads + 1 ||
count == kKernelThreads + 2);
for (size_t i = 0;
i < kKernelThreads + 1; i++) {
workers.emplace_back([&] {
Parcel localReply;
server->transact(
BINDER_LIB_TEST_LOCK_UNLOCK,
data, &localReply);
});
}测试允许 +1/+2 的竞态,随后占用大量 worker、延迟解锁并在最终断言总量。它证明计数是带竞态的 池容量模型,不支持“恒定线程数”结论。
启动状态
相关测试:ThreadPoolStarted()
服务端返回 ProcessState.isThreadPoolStarted,客户端断言 true。它只证明 server harness 已调用 startThreadPool,不证明当前所有 worker 都已创建或空闲。
优先级测试
BinderThreadPriorityTest 的优先级传播用例当前带 SkipPresubmit,不能作为稳定运行证据;源码仍可 用来发现“调用优先级”和“回调后恢复”问题,但公开结论必须回到驱动 priority 代码或重新执行测试。
12. 阅读边界
线程状态图
waiting_threads 是驱动 proc work 的消费者候选集合;mExecutingThreadsCount 是 Native 执行计数; requested_threads 是尚未注册的请求。三者不等价,排查线程池问题时必须分别读取。
本文证明了 TLS、线程池启动、ENTER/REGISTER、waiting_threads、proc/thread todo、动态扩容、 计数竞态、poll 模式、退出和饥饿边界。没有展开完整优先级继承、嵌套死锁算法、oneway 限流、 Java Binder 线程命名、system_server 专用线程和调试工具。
下一篇进入驱动层 BD013。复查线程问题时按“线程入口、looper bits、transaction_stack、 thread.todo/proc.todo、waiting_threads、requested/max、退出状态”取证。
