本文源代码基于 Android 7.0。
本文主要分析 Binder 线程池以及 Binder 线程启动过程。
目录:
1. 概述
Android 系统启动完成后,ActivityManager,PackageManager 等各大服务都运行在 system server 进程, app 应用需要使用系统服务都是通过 binder 来完成进程之间的通信。Binder 线程是如何管理的呢,又是如何创建的呢? 其实无论是system server 进程,还是 app 进程,都是在进程 fork 完成后,便会在新进程中执行 onZygoteInit() 的过程中, 启动 Binder 线程池。接下来,就以此为起点展开从线程的视角来看看 Binder 的世界。
2. 分析
Binder 线程于其所在进程的创建中产生,Java 层进程的创建都是通过 Process.start() 方法,向 Zygote 进程发出创建进程的 Socket 消息, Zygote 收到消息后会调用 Zygote.forkAndSpecialize() 来 fork 出新进程,在新进程中会调用到RuntimeInit.nativeZygoteInit 方法, 该方法经过 JNI 映射,最终会调用到 app_main.cpp 中的 onZygoteInit,那么接下来从这个方法说起。
/base/cmds/app_process/app_main.cpp
virtual void onZygoteInit()
{
sp proc = ProcessState::self();
proc->startThreadPool();
}
做了两件事:
ProcessState::self() 是单例模式,主要工作是调用 open() 打开 /dev/binder 驱动设备,再利用 mmap() 映射内核的地址空间, 将Binder 驱动的 fd 赋值 ProcessState 对象中的变量 mDriverFD,用于交互操作。
startThreadPool() 是创建一个新的 Binder 线程池,不断进行 talkWithDriver()。
/native/libs/binder/ProcessState.cpp
void ProcessState::startThreadPool()
{
AutoMutex _l(mLock);
if (!mThreadPoolStarted) {
mThreadPoolStarted = true;
spawnPooledThread(true);
}
}
启动 Binder 线程池后, 则设置 mThreadPoolStarted=true。通过变量 mThreadPoolStarted 来保证每个应用进程只允许启动一个Binder 线程池,且本次创建的是 Binder 主线程 (isMain=true)。其余 Binder 线程池中的线程都是由 Binder 驱动来控制创建的。
也就是说每次 fork 出一个新进程,都会启动一个 Binder 线程池,只允许启动一个 Binder 线程池。
/native/libs/binder/ProcessState.cpp
void ProcessState::spawnPooledThread(bool isMain)
{
if (mThreadPoolStarted) {
String8 name = makeBinderThreadName();
ALOGV("Spawning new pooled thread, name=%s\n", name.string());
sp t = new PoolThread(isMain);
t->run(name.string());
}
}
获取 Binder 线程名,格式为 Binderx,其中 x 为整数。每个进程中的 Binder 编码是从 1 开始,依次递增。只有通过spawnPooledThread 方法来创建的线程才符合这个格式,对于直接将当前线程通过 joinThreadPool 加入线程池的线程名则不符合这个命名规则。 另外,目前 Android N 中 Binder 命令已改为 Binder:x 格式,则对于分析问题很有帮忙,通过 Binder 名称的pid 字段可以快速定位该 Binder 线程所属的进程 p。
/native/libs/binder/ProcessState.cpp
class PoolThread : public Thread
{
public:
PoolThread(bool isMain)
: mIsMain(isMain)
{
}
protected:
virtual bool threadLoop()
{
IPCThreadState::self()->joinThreadPool(mIsMain);
return false;
}
const bool mIsMain;
};
从函数名看起来是创建线程池,其实就只是创建一个线程,该 PoolThread 继承 Thread 类。t->run() 方法最终调用 PoolThread的 threadLoop() 方法。
/native/libs/binder/IPCThreadState.cpp
void IPCThreadState::joinThreadPool(bool isMain)
{
// ...
do {
// 清除队列的引用
processPendingDerefs();
// 处理下一条指令
result = getAndExecuteCommand();
if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
mProcess->mDriverFD, result);
abort();
}
// 非主线程出现timeout则线程退出
if(result == TIMED_OUT && !isMain) {
break;
}
} while (result != -ECONNREFUSED && result != -EBADF);
// 线程退出循环
mOut.writeInt32(BC_EXIT_LOOPER);
// false 代表 bwr 数据的 read_buffer 为空
talkWithDriver(false);
}
对于 isMain=true 的情况下, command 为 BCENTERLOOPER,代表的是 Binder 主线程,不会退出的线程。对于 isMain=false 的情况下,command 为 BCREGISTERLOOPER,表示是由 Binder 驱动创建的线程。
默认地,每个进程的 Binder 线程池的线程个数上限为 15,该上限不统计通过 BCENTERLOOPER 命令创建的 Binder 主线程, 只计算 BCREGISTERLOOPER 命令创建的线程。 对此,或者很多人不理解,例个例子:某个进程的主线程执行如下方法,那么该进程可创建的 Binder 线程个数上限是多少呢?
ProcessState::self()->setThreadPoolMaxThreadCount(6); // 6个线程 ProcessState::self()->startThreadPool(); // 1个线程 IPCThread::self()->joinThreadPool(); // 1个线程
首先线程池的 Binder 线程个数上限为 6 个,通过 startThreadPool() 创建的主线程不算在最大线程上限,最后一句是将当前线程成为 Binder 线程, 所以说可创建的 Binder 线程个数上限为8,如果还不理解,建议再多看看这几个方案的源码,多思考整个 Binder 架构。
举个例子来理解下:
Client 进程 A 中用户线程 1 -> 内核态记录进程线程信息 -> transaction 发送数据 -> 保存到目标进程的队列。
Client 进程 A 中用户线程 2 -> 内核态记录进程线程信息 -> transaction 发送数据 -> 保存到目标进程的队列。
Client 进程 A 中用户线程 3 -> 内核态记录进程线程信息 -> transaction 发送数据->保存到目标进程的队列。
假如线程 1 在目标进程的队列第一个,线程 2 在目标进程的队列第二个,线程 3 在目标进程的队列第三个。
Binder 线程是用户空间创建,内核空间控制,线程在内核空间的红黑树上。用户空间创建的线程,加入线程池中,设置调度策略(一般是分时调度策略)和优先级别,假设 Service 端创建 Binder 线程:Binder 线程 1、Binder 线程 2、Binder 线程 3。
Binder 线程信息:
struct binder_thread {
...
struct binder_transaction *transaction_stack; //要发送和接收进程和线程的信息
...
}
调度合适的 Binder 线程:
3. 总结
Binder 设计架构中,只有第一个 Binder 主线程 (也就是 Binder_1 线程) 是由应用程序主动创建,Binder 线程池的普通线程都是由Binder 驱动根据 IPC 通信需求创建,Binder 线程的创建流程图:
每次由 Zygote fork 出新进程的过程中,伴随着创建 Binder 线程池,调用 spawnPooledThread 来创建 Binder 主线程。 当线程执行 binderthreadread 的过程中,发现当前没有空闲线程,没有请求创建线程,且没有达到上限,则创建新的 Binder 线程。
Binder 的 transaction 有 3 种类型:
Binder 系统中可分为 3 类 Binder 线程: