ServiceManager启动
ServiceManager 启动并不是“new 一个对象后调用 joinThreadPool()”。Android 17 的 servicemanager 进程先初始化 /dev/binder 对应的 ProcessState,明确禁止普通同步 Binder 调用和额外线程池线程;随后创建本地 ServiceManager,先把它登记为名为 manager 的服务和 context object,再通过 becomeContextManager() 占据 Binder 驱动的 context-manager 角色。运行阶段由 Looper 同时轮询 Binder fd 和一个 client-callback timerfd。
本文面向已经读过 Binder设备打开、BINDER_WRITE_READ命令、Binder线程管理 和 Binder死亡通知 的读者。本文只讲 AOSP servicemanager 进程如何达到“可处理服务目录请求”的状态,不提前展开 addService()、getService()、VINTF accessor 或 lazy service 的具体业务规则。
读完后,应该能从 init rc 定义找到可执行程序,再从 main() 解释为什么 setThreadPoolMaxThreadCount(0) 不代表进程不会收 Binder 请求,为什么 setTheContextObject() 与 becomeContextManager() 缺一不可,以及 Binder fd 回调、定时器回调和 ready property 分别在何时生效。
1. 启动边界
1.1 Init服务
源码文件:frameworks/native/cmds/servicemanager/servicemanager.rc
service servicemanager /system/bin/servicemanager
class core animation
user system
group system readproc
critical
file /dev/kmsg w
onrestart setprop servicemanager.ready false
onrestart restart --only-if-running apexd
onrestart restart audioserver
onrestart restart gatekeeperd
onrestart class_restart --only-enabled main
onrestart class_restart --only-enabled hal
onrestart class_restart --only-enabled early_hal
task_profiles ProcessCapacityHigh
shutdown critical这份 rc 文件定义 AOSP 系统镜像中的 servicemanager 服务:它属于 core animation class,以 system 用户运行,并在重启时先清除 ready property,再重启一组依赖服务。critical 和 shutdown critical 是 init 对此服务的策略,不是 Binder 驱动的 context-manager 协议字段;设备厂商可以在自己的系统配置中加入额外策略。
1.2 可执行目标
源码文件:frameworks/native/cmds/servicemanager/Android.bp
相关模块:servicemanager
Android.bp 将 main.cpp、ServiceManager.cpp 和 Access.cpp 组成 servicemanager 可执行程序,并链接 binder、utils、base 等库。这里的可执行模块与 ServiceManager C++ 类不是同一个概念:前者由 init 启动,后者是该进程内接受 Binder 调用的本地对象。
2. Main顺序
2.1 参数与日志
源码文件:frameworks/native/cmds/servicemanager/main.cpp
相关函数:main()
android::base::InitLogging(
argv, android::base::KernelLogger);
if (argc > 2) {
LOG(FATAL) << "usage: "
<< argv[0]
<< " [binder driver]";
}
const char* driver = argc == 2
? argv[1] : "/dev/binder";
LOG(INFO) << "Starting sm instance on "
<< driver;程序只接受零或一个 Binder driver 路径参数。默认路径是 /dev/binder;参数检查失败会在任何 Binder 状态建立之前 fatal。日志采用 KernelLogger,这影响观测位置,不改变 ServiceManager 的 Binder 协议。
2.2 主线图
图中的两个 callback 共用同一 Looper,但职责不同:Binder fd 有输入时消费驱动命令,timerfd 到期时检查服务的 client callback 状态。前者是服务目录 Binder 请求的入口,后者不是 Binder transaction 的替代路径。
3. Binder准备
3.1 ProcessState
源码文件:frameworks/native/cmds/servicemanager/main.cpp
相关函数:main()
sp<ProcessState> ps =
ProcessState::initWithDriver(driver);
ps->setThreadPoolMaxThreadCount(0);
ps->setCallRestriction(
ProcessState::CallRestriction::
FATAL_IF_NOT_ONEWAY);
IPCThreadState::self()->
disableBackgroundScheduling(true);initWithDriver() 建立本进程的 Binder driver 与 VMA 状态。线程池上限设为 0 表示 ServiceManager 不依赖 startThreadPool()/joinThreadPool() 创建额外 Binder worker;它仍可通过下文的 setupPolling() 在主 Looper 线程处理 Binder fd。调用限制是本进程作为调用方的约束,防止 ServiceManager 主动做普通同步 Binder 调用;它不阻止其他进程同步调用 ServiceManager。
禁用后台调度影响当前 IPCThreadState 的调度行为,不能简化为“ServiceManager 永远只有一个 Linux 线程”。本篇只证明主服务循环的 Binder 消费由 Looper 注册的 fd 承担。
3.2 本地对象
源码文件:frameworks/native/cmds/servicemanager/main.cpp
相关类型:ServiceManager、Access
sp<ServiceManager> manager =
sp<ServiceManager>::make(
std::make_unique<Access>());
manager->setRequestingSid(true);
if (!manager->addService(
"manager", manager,
false /*allowIsolated*/,
IServiceManager::
DUMP_FLAG_PRIORITY_DEFAULT)
.isOk()) {
LOG(ERROR) << "Could not self register "
<< "servicemanager";
}manager 是一个本地 Binder 实体,Access 是服务目录 API 的权限判断 owner。setRequestingSid(true) 要求 transaction 交付时带上 SELinux security context;自注册把名称 manager 放入 ServiceManager 自己的服务表,为同进程服务目录 API 提供普通名字查询入口。日志错误不立即终止,后续 context-manager 注册仍会执行。
3.3 Context角色
源码文件:frameworks/native/cmds/servicemanager/main.cpp
相关函数:IPCThreadState::setTheContextObject()、ProcessState::becomeContextManager()
IPCThreadState::self()->
setTheContextObject(manager);
if (!ps->becomeContextManager()) {
LOG(FATAL) << "Could not become "
<< "context manager";
}这两步的 owner 不同:setTheContextObject() 设置本进程用户态在收到 context-manager transaction 时要分发给的 BBinder 对象;becomeContextManager() 通过 driver 协议申请全局 context-manager 角色。只有前者会留下本地分发对象,只有后者会让其他进程的 handle 0 请求被驱动路由到该 Binder context。后者失败会 fatal,因为没有 context-manager 的 ServiceManager 不能承担目录角色。
4. Looper消费
4.1 Binder fd
源码文件:frameworks/native/cmds/servicemanager/main.cpp
相关类型:BinderCallback
IPCThreadState::self()->setupPolling(
&cb->mBinderFd);
LOG_ALWAYS_FATAL_IF(cb->mBinderFd < 0,
"Failed to setupPolling: %d",
cb->mBinderFd);
int ret = looper->addFd(
cb->mBinderFd,
Looper::POLL_CALLBACK,
Looper::EVENT_INPUT, cb, nullptr);
LOG_ALWAYS_FATAL_IF(ret != 1,
"Failed to add binder FD to Looper");setupPolling() 把当前 IPCThreadState 准备为可轮询的 Binder fd;Looper::addFd() 只注册输入事件,尚未执行 transaction。fd 无效或注册失败都会 fatal,因为主循环无法再接收 Binder work。
4.2 Binder回调
源码文件:frameworks/native/cmds/servicemanager/main.cpp
相关函数:BinderCallback::handleEvent()
int handleEvent(int /*fd*/, int /*events*/,
void* /*data*/) override {
IPCThreadState::self()->
handlePolledCommands();
return 1;
}当 Binder fd 可读时,Looper 调用这个 callback;handlePolledCommands() 再读取 BR 命令、把请求分发给 context object 或处理其他 Binder command。返回 1 表示 callback 保持注册,因而不是“一次处理后退出”的线程池循环。
4.3 Client定时器
源码文件:frameworks/native/cmds/servicemanager/main.cpp
相关类型:ClientCallbackCallback
itimerspec timespec {
.it_interval = { .tv_sec = 5 },
.it_value = { .tv_sec = 5 },
};
timerfd_settime(fdTimer, 0, ×pec, nullptr);
int handleEvent(int fd, int /*events*/,
void* /*data*/) override {
uint64_t expirations;
read(fd, &expirations, sizeof(expirations));
mManager->handleClientCallbacks();
mBinderCallback->repoll();
return 1;
}定时器每 5 秒触发一次 ServiceManager 的 client callback 检查,再显式 repoll Binder fd。它服务于 lazy service/client 状态,而不是周期性扫描服务注册表的通用机制;timerfd 读取失败只记录错误,callback 仍返回 1 继续运行。
5. Ready与循环
5.1 Ready属性
源码文件:frameworks/native/cmds/servicemanager/main.cpp
#ifndef VENDORSERVICEMANAGER
if (!SetProperty(
"servicemanager.ready", "true")) {
LOG(ERROR) << "Failed to set "
<< "servicemanager ready property";
}
#endif默认 ServiceManager 在 context-manager 成功、Looper callback 与 client callback timer 建立之后才尝试发布 ready;编译为 VENDORSERVICEMANAGER 时这段不参与。SetProperty 失败只记录错误,主循环仍然启动,因此 property true 是启动完成的可观测信号,不是 Binder driver 本身的有效性证明。
5.2 无限轮询
源码文件:frameworks/native/cmds/servicemanager/main.cpp
while (true) {
looper->pollAll(-1);
}pollAll(-1) 无限等待 fd 事件。正常路径没有“启动函数返回成功”的阶段:进程一旦进入循环,就通过 Binder callback 和 timer callback 重复处理工作。fatal 失败发生在进入循环前的 Binder fd、context-manager 或 timer 注册关键步骤。
6. 失败与恢复
| 阶段 | 条件 | 可观察结果 | 后续状态 |
|---|---|---|---|
| 参数 | 多于一个 driver 参数 | LOG(FATAL) | 未打开 Binder |
| Context角色 | becomeContextManager() 失败 | LOG(FATAL) | 不进入 Looper |
| Binder轮询 | fd 无效或 addFd 失败 | LOG_ALWAYS_FATAL_IF | 不进入主循环 |
| Timer | timerfd_create/settime/addFd 失败 | LOG_ALWAYS_FATAL_IF | 不发布 ready |
| 自注册 | addService("manager") 失败 | 记录 error | 继续尝试 context-manager |
| Ready属性 | SetProperty 失败 | 记录 error | 仍进入 pollAll |
| 进程重启 | init service 重启规则 | ready 先置 false | init 重建进程状态 |
ServiceManager 的 init critical 策略与内部 LOG(FATAL) 需要区分:前者决定 init 如何处理进程反复失败,后者是当前进程是否继续运行的代码路径。本文不从 rc 文件推导设备实际重启阈值或厂商重启策略。
7. 测试边界
7.1 名称工具
源码文件:frameworks/native/cmds/servicemanager/ServiceManagerUnittest.cpp
相关测试:ServiceManager.NativeName、NativeName_Malformed
这些 unit test 覆盖 native service 名称转换与格式错误,不启动真实 main(),也不打开 Binder driver、申请 context-manager 或运行 Looper。它们能反向约束 ServiceManager 的名称辅助逻辑,但不能证明启动顺序。
7.2 Main边界
当前 cmds/servicemanager 的单元测试和 fuzz target 主要覆盖 ServiceManager API、名称处理和输入健壮性;在固定源码范围内没有找到直接执行生产 main()、真实 becomeContextManager()、Binder fd poll 与 ready property 的单元测试。本文对这些步骤只依据源码控制流和 fatal/错误分支,不把未执行的设备启动写成运行观察。
7.3 可执行验证
读者可先从源码复现顺序:
rg -n "initWithDriver|setThreadPoolMaxThreadCount|setTheContextObject|becomeContextManager|pollAll" \
frameworks/native/cmds/servicemanager/main.cpp
rg -n "setupPolling|handlePolledCommands|timerfd_create|handleClientCallbacks|repoll" \
frameworks/native/cmds/servicemanager/main.cpp
rg -n "service servicemanager|critical|onrestart|servicemanager.ready" \
frameworks/native/cmds/servicemanager/servicemanager.rc
rg -n "NativeName|Malformed" \
frameworks/native/cmds/servicemanager/ServiceManagerUnittest.cpp如果能解释“为什么 0 线程池仍然能服务 Binder 请求”“为什么 context object 和 context-manager 注册各自不可替代”“为什么 ready property 在 Looper 建立后才设置”“为什么自注册失败与 context-manager fatal 是不同级别的错误”,就已经掌握了 ServiceManager 启动的真实边界。
