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引用

我们来看一个典型的流程:

  1. Service端启动:比如MediaPlayerService启动后,调用defaultServiceManager()->addService("media.player", this)
  2. ServiceManager登记:收到请求后,把"media.player"这个字符串和对应的Binder引用存到内部列表
  3. Client端查询:App想播放音乐,调用getService("media.player")
  4. 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的创建过程,到时候我们细聊。