在往期笔记中,通过小型系统启动日志梳理了 OHOS 的启动流程,并认识到可以从 init 进程拉起的相关服务进程进一步探究 OHOS 世界。本篇笔记则是对 appspawn 进程的初步探究
其官方仓为 https://gitee.com/openharmony/startup_appspawn_lite/tree/master
从参考资料可以知道,appspawn 应用孵化器组件其功能如其名,就是用来启动 OHOS app 的。appspawn 的源码其实很少,因此本篇笔记不会很长。具体地,需要对 samgr 系统服务框架有所了解,因为 appspawn 启动后,是作为 appspawn service 对外提供服务的。在往期博文中,笔者对 samgr 相关内容也做了简单梳理。
appspawn 的入口函数 main 很简单,主要执行了一些初始化,初始化完成后进入死循环
int main(int argc, char * const argv[])
{
sleep(1);
HILOG_INFO(HILOG_MODULE_HIVIEW, "[appspawn] main, enter.");
// 1. ipc module init
HOS_SystemInit();
// 2. register signal for SIGCHLD
SignalRegist();
// 3. keep process alive
HILOG_INFO(HILOG_MODULE_HIVIEW, "[appspawn] main, entering wait.");
while (1) {
// pause only returns when a signal was caught and the signal-catching function returned.
// pause only returns -1, no need to process the return value.
(void)pause();
}
}
了解 sagmr 服务注册方法可以知道,通过 SYSEX_SERVICE_INIT 可以将服务注册过程置于 main 函数前执行。因此探究 appspawn 执行过程应该从 appspawn_service.c 开始
AppSpawnInit 完成了 appspawn 服务注册。注册方法都是一样的,只需继承 service 接口,并定义一个 AppSpawnService 类即可,再将其通过 RegisterService 注册到 samgr 中。需要关注的是 service 每个类成员函数的具体实现
startup_appspawn_lite-master\services\src\appspawn_service.c
static AppSpawnService g_appSpawnService = {
.GetName = GetName,
.Initialize = Initialize,
.MessageHandle = MessageHandle,
.GetTaskConfig = GetTaskConfig,
SERVER_IPROXY_IMPL_BEGIN,
.Invoke = Invoke,
IPROXY_END,
};
void AppSpawnInit(void)
{
if (SAMGR_GetInstance()->RegisterService((Service *)&g_appSpawnService) != TRUE) {
HILOG_ERROR(HILOG_MODULE_HIVIEW, "[appspawn] register service failed!");
return;
}
HILOG_INFO(HILOG_MODULE_HIVIEW, "[appspawn] register service succeed. %{public}p.", &g_appSpawnService);
if (SAMGR_GetInstance()->RegisterDefaultFeatureApi(APPSPAWN_SERVICE_NAME, \
GET_IUNKNOWN(g_appSpawnService)) != TRUE) {
(void)SAMGR_GetInstance()->UnregisterService(APPSPAWN_SERVICE_NAME);
HILOG_ERROR(HILOG_MODULE_HIVIEW, "[appspawn] register featureapi failed!");
return;
}
HILOG_INFO(HILOG_MODULE_HIVIEW, "[appspawn] register featureapi succeed.");
}
SYSEX_SERVICE_INIT(AppSpawnInit);
主要关注其 Invoke 实现。Invoke 涉及 IServerProxy,因此我们可以知道是与服务的远程调用相关,具体怎么实现远程调用的不做探讨。具体地,先是根据请求 req 调用 GetMessageSt 获取 MessageSt。并根据 MessageSt 调用 CreateProcess 创建一个进程
static int Invoke(IServerProxy* iProxy, int funcId, void* origin, IpcIo* req, IpcIo* reply)
{
// ---- 省略部分代码 ------
MessageSt msgSt = {0};
if (GetMessageSt(&msgSt, req) != EC_SUCCESS) {
HILOG_ERROR(HILOG_MODULE_HIVIEW, "[appspawn] invoke, parse failed! reply %{public}d.", INVALID_PID);
IpcIoPushInt64(reply, INVALID_PID);
return EC_FAILURE;
}
HILOG_INFO(HILOG_MODULE_HIVIEW, "[appspawn] invoke, msg<%{public}s,%{public}s,%{public}d,%{public}d>", \
msgSt.bundleName, msgSt.identityID, msgSt.uID, msgSt.gID);
pid_t newPid = CreateProcess(&msgSt);
FreeMessageSt(&msgSt);
IpcIoPushInt64(reply, newPid);
// -------省略部分代码---------
return ((newPid > 0) ? EC_SUCCESS : EC_FAILURE);
}
进入 CreateProcess 可以看到,该函数首先执行了 fork,创建子进程后在子进程内调用了 AbilityMain,并传入了一个 identityID 。而 AbilityMain 便是 app 框架的入口函数。
pid_t CreateProcess(const MessageSt* msgSt)
{
pid_t newPID = fork();
if (newPID < 0) {
HILOG_ERROR(HILOG_MODULE_HIVIEW, "[appspawn] create process, fork failed! err %{public}d.", errno);
return -1;
}
// in child process
if (newPID == 0) {
// set permissions
if (SetPerms(msgSt->uID, msgSt->gID, msgSt->capsCnt, msgSt->caps) != 0) {
HILOG_ERROR(HILOG_MODULE_HIVIEW, "[appspawn] sub-process %{public}s exit!", msgSt->bundleName);
exit(0x7f); // 0x7f: user specified
}
(void)prctl(PR_SET_NAME, msgSt->bundleName);
if (AbilityMain(msgSt->identityID) != 0) {
HILOG_ERROR(HILOG_MODULE_HIVIEW, "[appspawn] AbilityMain execute failed, pid %{public}d.", getpid());
exit(0x7f); // 0x7f: user specified
}
HILOG_ERROR(HILOG_MODULE_HIVIEW, "[appspawn] sub-process exit, pid %{public}d.", getpid());
exit(0x7f); // 0x7f: user specified
}
return newPID;
}
至此,我们应该能大致清楚 appspawn 为什么称作应用孵化器组件了。同时,我们也可以猜测,执行 app 时会远程调用 appspawn,并将具体地 app 请求发给该服务。触发 appspawn 的 invoke 方法,为该 app 创建一个进程,并执行 AbilityMain。通过 appspawn 间接为 app 创建进程而不是让 app 自己创建进程的好处应该是能够更好地进行权限控制。