Binder线程池初始化流程
Binder线程池的初始化始于ServiceManager进程的启动。作为Android系统的服务注册与发现中心,ServiceManager在启动时会完成binder设备节点的打开、内存映射、默认线程数配置,随后进入循环等待接收客户端请求。理解这一初始化流程,是分析与调优Binder线程池的基础。
2.1 整体初始化流程
ServiceManager的主函数执行顺序如下:
- 打开binder设备:调用
binder_open打开/dev/binder设备节点。 - 内存映射:调用
binder_mmap将内核缓冲区映射到用户空间,默认映射大小为128KB。 - 设置默认线程数:通过
ioctl(BC_SET_WAKEUP_NUM)或内部默认值配置线程池初始规模。 - 进入主循环:调用
binder_loop开始处理binder事务。
流程图展示了关键阶段与系统调用之间的关系:
关键知识点:ServiceManager是binder上下文管理者,它自身也使用binder线程池接收服务注册与查询请求。默认线程数决定了ServiceManager能够并发处理的事务数量,直接影响系统启动过程中的服务注册吞吐量。
调优提示:在早期Android版本(如Android 4.x)中,ServiceManager的默认线程池大小为1;从Android 5.0开始提升为4。针对低内存设备可适当降低此值以减少开销,但需注意可能引发服务注册拥塞。
2.2 源码关键点分析
ServiceManager的初始化代码位于 frameworks/native/cmds/servicemanager/service_manager.c,核心函数如下:
int main(int argc, char **argv) {
struct binder_state *bs;
bs = binder_open("/dev/binder", 128*1024); // 默认mmap大小128KB
if (!bs) {
ALOGE("binder_open failed\n");
return -1;
}
// 设置线程池默认线程数
// BEFORE: bc = BINDER_THREAD_POOL_INIT(1)
// AFTER: bc = BINDER_THREAD_POOL_INIT(4)
binder_set_max_threads(bs, 4);
// 注册服务管理器实体
svcmgr_handle = svcmgr_get(bs);
svcmgr_handler = svcmgr_handler_func;
// 进入binder循环(内部开始接收事务)
binder_loop(bs, svcmgr_handler);
return 0;
}
binder_open 底层通过 open() 打开字符设备,返回binder句柄。
常见错误:若 binder_mmap 失败(通常因内存不足或映射大小不合法),ServiceManager无法启动,系统将无法注册任何服务,表现为开机后所有系统服务异常。
2.3 默认线程数的演变与配置
| Android版本 | ServiceManager默认线程数 | 原因/说明 |
|---|---|---|
| 4.x及之前 | 1 | 单线程处理所有服务注册请求,容易成为瓶颈 |
| 5.0 - 7.x | 4 | 提升并发处理能力,平衡内存占用 |
| 8.0 + (Treble) | 4 | 架构调整后仍然保持4,支持hwbinder额外线程池 |
ServiceManager的默认线程数在源码中通过宏定义指定:
// frameworks/native/cmds/servicemanager/binder.c
#define DEFAULT_MAX_THREADS 4
void binder_set_max_threads(struct binder_state *bs, int max_threads) {
struct binder_set_threads threads;
threads.max_threads = max_threads;
ioctl(bs->fd, BINDER_SET_MAX_THREADS, &threads);
}
线程池初始化的核心机制是:用户空间通过 ioctl 告知内核期望的最多并发线程数(即Binder线程池容量)。内核在接收到 BC_TRANSACTION 且当前活跃线程数未达上限时,会唤醒用户空间的binder loop创建新线程(通过 BC_ENTER_LOOPER 状态机),直到线程数达到设定上限。
2.4 binder_mmap 线程池内存布局
binder_mmap 映射的128KB内存不仅用于数据传递,还隐含线程池缓冲区管理机制。映射区域被划分为两个部分:
- 物理页数组:用于存储binder事务的数据(每个事务最多约4KB)。
- 内核态/用户态共享状态:记录已分配事务缓冲区的偏移与大小。
默认128KB映射大小最多支撑约32个并发tranaction(假设每个事务占用单个4KB页)。当系统服务密集注册时,若映射空间耗尽,内核将返回 BR_FAILED_REPLY,服务端需等待缓冲释放——这也是调优中需要关注的瓶颈之一。
// frameworks/native/include/binder/MemoryHeapBase.h (用户态示例)
#define BINDER_VM_SIZE ((size_t)(128 * 1024))
2.5 初始化阶段的监控与日志
通过logcat可以观察到ServiceManager初始化时的线程配置信息:
# 示例日志(Android 12 模拟器)
I/servicemanager( 1234): ServiceManager starting with 4 threads
I/servicemanager( 1234): binder mmap size 131072 (128 KB)
D/servicemanager( 1234): binder_open /dev/binder fd=3
若线程数异常(如显示负数或0),说明配置未生效或ioctl调用失败,需要检查selinux权限或内核binder驱动版本。
稳定性优化建议:在 init.rc 中启动servicemanager时,可添加 rlimit 调整允许的最大线程数,避免因进程默认线程上限过低导致binder无法创建新线程,从而引发 No more threads 错误。