一、ServiceManager 到底是什么角色?

说实话,我刚接触 Android Binder 时,对 ServiceManager 的理解很模糊。当时我就想:这不就是个注册中心吗?后来深入源码才发现,它的地位远比我想象的重要。

ServiceManager 在 Binder 通信中扮演的角色,说白了就是「电话总机」。你想啊,一个 App 要调用系统的某个服务(比如 ActivityManagerService),它怎么知道这个服务在哪个进程?怎么拿到对应的 Binder 引用?

嗯,这就是 ServiceManager 要解决的问题。它维护了一张全局的服务注册表,所有服务都要先到它这里报到,客户端才能通过它找到目标服务。

核心要点:ServiceManager 是 Binder 通信的「名字服务」+「路由中心」。它把服务的字符串名称映射到 Binder 引用,让客户端可以按名寻址。

二、ServiceManager 的启动流程

我记得第一次看 init.rc 时,发现 ServiceManager 的启动优先级非常高。它是在 init 进程启动后,第一个被启动的 native 服务。

启动流程大致是这样的:

  1. init 进程解析 init.rc,找到 servicemanager 的启动配置
  2. fork 出子进程,执行 /system/bin/servicemanager 可执行文件
  3. servicemanager 进入 main 函数,调用 binder_open() 打开 Binder 驱动
  4. 调用 binder_become_context_manager() 把自己注册为「上下文管理器」
  5. 进入无限循环,调用 binder_loop() 等待客户端请求

这里有个关键点:ServiceManager 本身也是一个 Binder 服务,但它的句柄是 0。为什么是 0?因为 Binder 驱动在初始化时,会把第一个注册的上下文管理器分配句柄 0。所有客户端都知道,只要向句柄 0 发送请求,就能找到 ServiceManager。

我个人习惯:在分析系统启动流程时,我会先看 init.rc 中 servicemanager 的启动顺序。如果它排在 zygote 之后,那系统大概率会出问题——因为所有 Java 服务都需要先通过 ServiceManager 注册。

三、服务注册流程

当一个服务(比如 MediaPlayerService)想要对外提供服务时,它需要先向 ServiceManager 注册。这个过程我画了一张图,你看完就明白了:

ServiceManager 服务注册与获取流程 服务端进程 (如 MediaPlayerService) ServiceManager (句柄 0) 客户端进程 (如 App) ① 注册服务 "media.player" ② 获取服务 "media.player" → Binder引用 ServiceManager 内部 svclist 链表 "media.player" "activity" "window" ...... 注:注册时传递服务名称 + Binder引用;获取时返回目标服务的Binder引用

注册流程的代码实现,核心在 service_manager.cdo_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 通信的其他部分,就会轻松很多。


公众号:蓝海资料掘金营,微信deep3321