3. ServiceManager启动与注册流程
3.1 ServiceManager的角色与启动入口
ServiceManager是Binder架构中的核心守护进程,充当所有系统服务的“电话簿”。它运行在独立进程中,通过打开Binder驱动进入等待请求的循环。客户端进程通过查询ServiceManager获取服务句柄,进而与该服务通信。
ServiceManager的入口位于frameworks/native/cmds/servicemanager/service_manager.c。其启动流程如下:
- 打开
/dev/binder设备节点,获取Binder文件描述符。 - 调用
ioctl设置Binder版本和线程池大小(通常为0,表示不创建额外线程)。 - 进入无限循环,通过
ioctl(BINDER_WRITE_READ)读取Binder驱动发来的请求。 - 根据请求类型分发处理:注册服务、查询服务、服务列表管理等。
// 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 found3.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发送注册请求。整个流程分为以下步骤:
- 打开Binder设备:服务进程调用
binder_open()获取Binder描述符。 - 构建注册事务:设置事务码为
SVC_MGR_ADD_SERVICE(对应0x0),数据为服务名称字符串。 - 发送BC_TRANSACTION:通过
ioctl(BINDER_WRITE_READ)将事务写入Binder驱动。 - 驱动转发至ServiceManager:Binder驱动根据目标句柄0(ServiceManager)将请求递送到ServiceManager进程。
- 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启动、等待请求、服务注册核心路径。