一、ServiceManager 到底是什么角色?
说实话,我刚接触 Android Binder 时,对 ServiceManager 的理解很模糊。当时我就想:这不就是个注册中心吗?后来深入源码才发现,它的地位远比我想象的重要。
ServiceManager 在 Binder 通信中扮演的角色,说白了就是「电话总机」。你想啊,一个 App 要调用系统的某个服务(比如 ActivityManagerService),它怎么知道这个服务在哪个进程?怎么拿到对应的 Binder 引用?
嗯,这就是 ServiceManager 要解决的问题。它维护了一张全局的服务注册表,所有服务都要先到它这里报到,客户端才能通过它找到目标服务。
核心要点:ServiceManager 是 Binder 通信的「名字服务」+「路由中心」。它把服务的字符串名称映射到 Binder 引用,让客户端可以按名寻址。
二、ServiceManager 的启动流程
我记得第一次看 init.rc 时,发现 ServiceManager 的启动优先级非常高。它是在 init 进程启动后,第一个被启动的 native 服务。
启动流程大致是这样的:
- init 进程解析 init.rc,找到 servicemanager 的启动配置
- fork 出子进程,执行 /system/bin/servicemanager 可执行文件
- servicemanager 进入 main 函数,调用 binder_open() 打开 Binder 驱动
- 调用 binder_become_context_manager() 把自己注册为「上下文管理器」
- 进入无限循环,调用 binder_loop() 等待客户端请求
这里有个关键点:ServiceManager 本身也是一个 Binder 服务,但它的句柄是 0。为什么是 0?因为 Binder 驱动在初始化时,会把第一个注册的上下文管理器分配句柄 0。所有客户端都知道,只要向句柄 0 发送请求,就能找到 ServiceManager。
我个人习惯:在分析系统启动流程时,我会先看 init.rc 中 servicemanager 的启动顺序。如果它排在 zygote 之后,那系统大概率会出问题——因为所有 Java 服务都需要先通过 ServiceManager 注册。
三、服务注册流程
当一个服务(比如 MediaPlayerService)想要对外提供服务时,它需要先向 ServiceManager 注册。这个过程我画了一张图,你看完就明白了:
注册流程的代码实现,核心在 service_manager.c 的 do_add_service() 函数中。我简化一下关键逻辑:
// 伪代码 - 服务注册核心逻辑
int do_add_service(struct binder_state *bs,
const uint16_t *s, size_t len,
struct binder_io *msg,
struct binder_io *reply) {
// 1. 解析服务名称
char *name = parse_service_name(s, len);
// 2. 从消息中提取 Binder 引用
struct binder_object *obj = parse_binder_object(msg);
// 3. 检查权限(只有系统服务才能注册)
if (!check_permission(calling_pid, calling_uid)) {
return PERMISSION_DENIED;
}
// 4. 查找是否已存在同名服务
struct svcinfo *si = find_svc(name);
if (si) {
// 已存在则更新
si->handle = obj->handle;
} else {
// 不存在则新建节点,插入 svclist 链表
si = malloc(sizeof(*si));
si->name = strdup(name);
si->handle = obj->handle;
list_add(&si->list, &svclist);
}
return 0;
}
我曾经踩过一个坑:在自定义 ROM 开发时,我写了一个 native 服务,注册时总是返回权限错误。查了半天才发现,ServiceManager 对注册进程的 uid 有要求——只有 system 和 root 用户才能注册服务。普通 App 进程想注册?门都没有。
四、服务获取流程
客户端获取服务的过程,其实就是向 ServiceManager 发送一个查询请求。我习惯把这个过程叫做「问路」——客户端问 ServiceManager:「你知道 media.player 在哪吗?」
获取流程的代码实现:
// 伪代码 - 服务获取核心逻辑
struct binder_object *do_find_service(struct binder_state *bs,
const uint16_t *s, size_t len) {
// 1. 解析服务名称
char *name = parse_service_name(s, len);
// 2. 遍历 svclist 链表查找
struct svcinfo *si = svclist;
while (si) {
if (strcmp(si->name, name) == 0) {
// 找到了!返回 Binder 引用
return &si->handle;
}
si = si->next;
}
// 3. 没找到,返回空
return NULL;
}
你想想看,这个查找过程其实很简单——就是遍历链表。但这里有个性能问题:如果服务数量很多(Android 系统中有上百个服务),每次查找都遍历链表,效率能高吗?
嗯,实际上 ServiceManager 的服务数量通常不会超过 200 个,线性查找完全够用。我在项目中测试过,一次查找耗时在微秒级别,不会成为瓶颈。
五、ServiceManager 的权限控制
ServiceManager 不是谁都能随便注册服务的。它有一套严格的权限检查机制:
| 操作 | 权限要求 | 说明 |
|---|---|---|
| 注册服务 | uid == AID_SYSTEM 或 AID_ROOT | 只有系统进程和 root 可以注册 |
| 获取服务 | 无限制 | 任何进程都可以查询 |
| 列出服务 | 无限制 | 任何进程都可以列出 |
避坑指南:我曾经在开发系统应用时,想注册一个自定义服务供其他 App 调用。结果发现普通 App 进程根本注册不了。后来我改用 Binder 的匿名服务(不经过 ServiceManager),通过 Intent 传递 Binder 引用,才解决了这个问题。
六、ServiceManager 的局限性
说实话,ServiceManager 的设计很简洁,但也有些局限性:
- 单点故障:ServiceManager 挂了,所有服务都找不到了。不过它本身很稳定,我几乎没见过它崩溃。
- 不支持服务发现:客户端必须提前知道服务名称,没法按类型或属性查找。
- 没有健康检查:服务进程挂了,ServiceManager 不会自动清理。不过 Binder 驱动会在服务死亡时发送死亡通知。
这些局限性在 Android 的演进中一直存在,但 Google 并没有大改 ServiceManager。为什么?因为它的设计哲学就是「够用就好」。复杂的服务发现和治理,应该由上层框架(比如 ServiceManager 的 Java 层封装)来解决。
好了,ServiceManager 的核心内容就这些。记住它的三个关键点:句柄 0、svclist 链表、权限控制。理解了这些,你再看 Binder 通信的其他部分,就会轻松很多。