2、ServiceManager的角色:它是什么?如何成为Binder通信的DNS?
好,我们直接切入正题。上一章我们聊了Binder的整体架构,很多朋友会问:那么多Service,系统怎么知道谁是谁?谁来管这个注册和查找?
答案就是——ServiceManager。
我个人习惯把它叫作“Binder世界的大管家”。你想想看,Android系统里跑着上百个服务,比如ActivityManagerService、WindowManagerService、PackageManagerService……如果没有一个统一的管理者,客户端想找个服务,那得满世界广播,效率低到没法用。
2.1 ServiceManager到底是什么?
从源码角度看,ServiceManager是一个用户空间的守护进程,它的二进制文件是/system/bin/servicemanager。但它又不仅仅是个普通进程——它是整个Binder通信的根节点。
为什么这么说?
因为ServiceManager本身也是一个Binder Service,但它有一个特殊身份:它是系统中第一个注册的Binder实体,句柄(handle)固定为0。所有其他Service想要被找到,必须先来找它报到。
核心要点:
- ServiceManager是一个独立的进程(servicemanager)
- 它是Binder通信的“名字服务器”
- 它的Binder句柄永远是0(特殊身份)
- 它维护着一张“服务名 → Binder引用”的映射表
嗯,这里要注意:ServiceManager本身不提供任何业务功能,它只做一件事——登记和查询。就像电话总机,它不负责通话内容,只负责帮你转接。
2.2 它如何成为Binder通信的DNS?
这个问题问得好。DNS(域名系统)把域名解析成IP地址,ServiceManager做的其实是类似的事:把字符串服务名解析成Binder引用。
我们来看一个典型的流程:
- Service端启动:比如MediaPlayerService启动后,调用
defaultServiceManager()->addService("media.player", this) - ServiceManager登记:收到请求后,把"media.player"这个字符串和对应的Binder引用存到内部列表
- Client端查询:App想播放音乐,调用
getService("media.player") - ServiceManager返回引用:Client拿到Binder引用后,就可以直接和MediaPlayerService通信了
说白了,这就是一个“先注册,后发现”的机制。我在项目中遇到过一个问题:某个自定义服务启动顺序不对,Client端调用getService时服务还没注册,结果返回了NULL,导致空指针崩溃。后来我加了个重试机制,才稳定下来。
2.3 核心数据结构:svclist
ServiceManager内部维护着一个链表,叫svclist。每个节点包含:
| 字段 | 说明 |
|---|---|
| name | 服务名称(字符串,如"activity") |
| handle | Binder句柄(整型,唯一标识) |
| ptr | 指向Binder实体的指针 |
| death | 死亡通知回调(服务挂了通知客户端) |
源码位置在frameworks/native/cmds/servicemanager/service_manager.c,核心代码其实很短:
struct svcinfo {
struct svcinfo *next; // 链表指针
uint32_t handle; // Binder句柄
struct binder_death *death;
int allow_isolated;
size_t len;
uint16_t name[0]; // 服务名(柔性数组)
};
你看,就是这么朴实无华的一个链表。但就是它,支撑起了整个Android系统的服务发现机制。
避坑指南:
我曾经在调试一个系统服务时,发现getService偶尔返回NULL。排查了半天,原来是ServiceManager的svclist链表被并发访问了。虽然ServiceManager是单线程处理请求,但某些极端情况下,addService和getService的时序问题还是会出现。我的建议是:客户端一定要做重试逻辑,不要假设一次就能拿到服务。
2.4 为什么说它是“DNS”?
我们来对比一下:
| DNS | ServiceManager |
|---|---|
| 域名 → IP地址 | 服务名 → Binder引用 |
| 全球分布式 | 单进程集中式 |
| 支持缓存、递归查询 | 只支持精确匹配 |
| 有超时、重试机制 | 客户端自己实现 |
虽然功能上类似,但ServiceManager要简单得多。它不做负载均衡,不做缓存,甚至不做权限校验(早期版本)。就是纯粹的“名字到引用”的映射。
我个人觉得,把它比作DNS其实有点抬举它了。更准确地说,它像是一个“电话本”——你查名字,它给你号码。但就是这个电话本,让Binder通信变得优雅而高效。
2.5 启动时序:为什么ServiceManager必须最先启动?
这个问题很关键。Android系统启动时,init进程会按顺序启动一系列守护进程。ServiceManager是第二个启动的(第一个是ueventd)。
为什么?
因为所有其他服务在初始化时,都要调用defaultServiceManager()来获取ServiceManager的代理。如果ServiceManager还没启动,这个调用就会失败。我见过一个定制系统,因为修改了init.rc的启动顺序,导致系统服务全部注册失败,开机直接黑屏。
警告:
千万不要随意修改ServiceManager的启动顺序!它是Binder通信的基石,必须在所有依赖Binder的服务之前启动。如果你在开发系统级服务,记得在onStart()中先检查ServiceManager是否可用。
2.6 小结
好了,这一章我们讲清楚了ServiceManager是什么:
- 它是一个特殊的Binder Service,句柄固定为0
- 它维护着服务名到Binder引用的映射表(svclist)
- 它充当了Binder通信的“DNS”角色,负责服务注册和发现
- 它必须在所有其他服务之前启动
下一章,我们会深入defaultServiceManager()的源码,看看客户端是如何拿到ServiceManager的代理的。嗯,那里有个很有意思的细节——BpBinder的创建过程,到时候我们细聊。