3. ServiceManager启动与注册流程

3.1 ServiceManager的角色与启动入口

ServiceManager是Binder架构中的核心守护进程,充当所有系统服务的“电话簿”。它运行在独立进程中,通过打开Binder驱动进入等待请求的循环。客户端进程通过查询ServiceManager获取服务句柄,进而与该服务通信。

ServiceManager的入口位于frameworks/native/cmds/servicemanager/service_manager.c。其启动流程如下:

  1. 打开/dev/binder设备节点,获取Binder文件描述符。
  2. 调用ioctl设置Binder版本和线程池大小(通常为0,表示不创建额外线程)。
  3. 进入无限循环,通过ioctl(BINDER_WRITE_READ)读取Binder驱动发来的请求。
  4. 根据请求类型分发处理:注册服务、查询服务、服务列表管理等。
// service_manager.c 核心流程简化
int main(int argc, char** argv) {
    struct binder_state *bs;
    bs = binder_open("/dev/binder", 128*1024); // 打开binder,接收128KB缓存
    if (!bs) {
        exit(1);
    }

    if (binder_become_context_manager(bs)) { // 成为上下文管理器
        exit(1);
    }

    // 主循环:处理传入的binder事务
    while (1) {
        struct binder_write_read bwr;
        // ... 从binder驱动读取数据
        // 解析事务,调用svcmgr_handler()
    }
    return 0;
}
ServiceManager启动后即成为Binder上下文管理器,所有进程都通过固定句柄0访问它。

3.2 BC_ACQUIRE / BC_INCREFS 引用计数协议

在Binder驱动层,BC_ACQUIRE和BC_INCREFS是用于管理binder节点引用计数的Binder命令。它们确保当某个进程持有代理对象时,对应进程不会意外退出,避免悬空引用。

Binder命令 作用 触发时机
BC_INCREFS 增加binder节点的弱引用计数(弱引用) 服务代理被创建、跨进程传递时
BC_ACQUIRE 增加binder节点的强引用计数(强引用) 代理对象被实际使用、建立连接时
BC_RELEASE 减少强引用计数 代理对象不再使用时
BC_DECREFS 减少弱引用计数 弱引用清理时

这些命令由Binder驱动负责执行,ServiceManager本身不使用它们管理自身,但在处理服务注册时,驱动会自动对ServiceManager的节点进行引用计数维护。当客户端注册服务时,驱动会向服务进程发送BC_ACQUIRE以保证服务进程不被释放。

调试要点:若遇到服务不可达或进程异常退出,可使用adb shell lsof | grep binder查看Binder句柄的引用计数状态,或通过dmesg查看内核中的引用计数错误日志:
Binder: release ref ×××, desc ××× not found

3.3 svcinfo 列表结构

ServiceManager内部维护一个服务信息链表svcinfo,每个节点记录一个已注册服务的元数据。数据结构如下:

struct svcinfo {
    struct svcinfo *next;          // 链表下一节点
    uint32_t handle;              // 服务在ServiceManager中的内部句柄
    struct binder_death *death;   // 死亡通知回调
    unsigned uid;                 // 注册服务的UID(用于权限检查)
    size_t len;                   // 服务名称长度
    char name[0];                 // 服务名称(变长数组)
};

链表头定义为全局变量svclist。ServiceManager每次收到注册请求时,会遍历该链表检查服务名是否已存在;若存在则更新句柄,若不存在则创建新节点插入链表。

3.4 服务注册流程详解

服务进程通过Binder向ServiceManager发送注册请求。整个流程分为以下步骤:

  1. 打开Binder设备:服务进程调用binder_open()获取Binder描述符。
  2. 构建注册事务:设置事务码为SVC_MGR_ADD_SERVICE(对应0x0),数据为服务名称字符串。
  3. 发送BC_TRANSACTION:通过ioctl(BINDER_WRITE_READ)将事务写入Binder驱动。
  4. 驱动转发至ServiceManager:Binder驱动根据目标句柄0(ServiceManager)将请求递送到ServiceManager进程。
  5. ServiceManager处理:调用svcmgr_handler()解析事务,创建/更新svcinfo节点,并将服务句柄返回给客户端(实际句柄由驱动分配)。

客户端发送注册请求的代码片段:

// 伪代码:服务进程注册自身
struct binder_transaction_data tr;
tr.code = SVC_MGR_ADD_SERVICE;
tr.target.handle = 0;  // 发送给ServiceManager
// 填入服务名称字符串
tr.data.ptr.buffer = svc_name;
tr.data_size = strlen(svc_name) + 1;

struct binder_write_read bwr;
bwr.write_buffer = &tr;
bwr.write_size = sizeof(tr);
ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

3.5 调试与常见错误

  • 服务名重复:ServiceManager不阻止同名服务注册,后注册的会覆盖之前的句柄。可通过adb shell svclist(或dumpsys service list)查看当前注册的服务列表。
  • 权限不足:ServiceManager仅在系统进程(UID = AID_SYSTEM,即1000)或root下允许注册服务。普通应用注册会被拒绝,日志显示Permission denied。
  • 句柄泄漏:若服务进程异常退出但未处理死亡通知,ServiceManager中svcinfo节点的handle可能无效。应注册死亡通知回调,监听BC_DEAD_BINDER_DONE。
  • 调试日志:在service_manager.c中启用LOG_NDEBUG=0宏可输出详细注册日志:
    svcinfo: add service 'myservice', handle = 0x6
注意:ServiceManager自身不生成新线程,所有请求都在主循环中串行处理。如果注册请求阻塞(例如Binder缓冲区满),会影响所有后续Binder操作。应避免在注册时进行耗时操作。

3.6 ServiceManager启动与注册流程总览

下面SVG图展示了ServiceManager启动、等待请求、服务注册核心路径。

ServiceManager启动与服务注册流程图 ServiceManager 进程 Binder 驱动 (Kernel) 服务进程 BC_TRANSACTION 转发请求 svcinfo 链表 node1: “myservice” → handle=0x6 node2: “surfaceflinger” → handle=0x1 node3: “activity” → handle=0x2 ... 服务注册处理 ① 检查服务名是否已存在 ② 创建/更新 svcinfo ③ 返回句柄给驱动 ④ 驱动缓存句柄映射 解包事务 插入/更新 说明:ServiceManager通过BC_TRANSACTION接收注册请求,内部维护svcinfo链表,并为每个服务分配唯一handle。客户端随后通过BC_ACQUIRE驱动引用计数保护服务进程。 BC_ACQUIRE / BC_INCREFS 引用计数管理