OpenHarmony启动流程
OpenHarmony 源码网站:
http://ci.openharmony.cn/codeSearch
https://www.lengqinjie.xyz/lxr/source/
一、kernel的启动
流程图:
由于OpenHarmony(下面简称OH)的标准系统的底层系统是linux,所以前期和Android系统的启动没什么太大区别。都是调用到如下的代码:
/kernel/linux/linux-5.10/init/main.c
noinline void __ref rest_init(void)
{
struct task_struct *tsk;
int pid;
rcu_scheduler_starting();
/*
* We need to spawn init first so that it obtains pid 1, however
* the init task will end up wanting to create kthreads, which, if
* we schedule it before we create kthreadd, will OOPS.
*/
//这里和Android一样都是启动init进程
pid = kernel_thread(kernel_init, NULL, CLONE_FS);
...
}
static int __ref kernel_init(void *unused)
{
...
//同样的和Android一样都是启动init进程
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh"))
return 0;
panic("No working init found. Try passing init= option to kernel. "
"See Linux Documentation/admin-guide/init.rst for guidance.");
}
由于OH标准系统是基于kernel内核开发的,所以上面的启动和Android是一样的,都是启动了init进程,那么OH的init进程的入口在哪里呢?答案是在
/base/startup/init/services/init/main.c中。
二、init进程的启动
在OH中BUILD.gn就相当于AOSP中的Android.mk或者Android.bp用于编译构建模块的。
/base/startup/init/services/init/standard/BUILD.gn
init_common_sources = [
"../init_capability.c",
"../init_common_cmds.c",
"../init_common_service.c",
"../init_config.c",
"../init_group_manager.c",
"../init_service_file.c",
"../init_service_manager.c",
"../init_service_socket.c",
"../main.c",
]
//编译可执行文件init
ohos_executable("init") {
sources = [
"../adapter/init_adapter.c",
"../standard/device.c",
"../standard/fd_holder_service.c",
"../standard/init.c",
"../standard/init_cmdexecutor.c",
"../standard/init_cmds.c",
"../standard/init_control_fd_service.c",
"../standard/init_jobs.c",
"../standard/init_mount.c",
"../standard/init_reboot.c",
"../standard/init_service.c",
"../standard/init_signal_handler.c",
"../standard/switch_root.c",
]
modulemgr_sources = [
"//base/startup/init/interfaces/innerkits/hookmgr/hookmgr.c",
"//base/startup/init/interfaces/innerkits/modulemgr/modulemgr.c",
]
sources += modulemgr_sources
sources += init_common_sources
从上面的BUILD.gn可以看到OH标准系统的init进程的入口就是init_common_sources的main.c也就是下面的代码
/base/startup/init/services/init/main.c
#include <signal.h>
#include "init.h"
#include "init_log.h"
static const pid_t INIT_PROCESS_PID = 1;
int main(int argc, char * const argv[])
{
int isSecondStage = 0;
(void)signal(SIGPIPE, SIG_IGN);
// Number of command line parameters is 2
//从kernel启动的init进程并未携带任何参数,这里是init的第一阶段
if (argc == 2 && (strcmp(argv[1], "--second-stage") == 0)) {
isSecondStage = 1;
}
if (getpid() != INIT_PROCESS_PID) {
INIT_LOGE("Process id error %d!", getpid());
return 0;
}
EnableInitLog(INIT_INFO);
//第一次这里走的是SystemPrepare
if (isSecondStage == 0) {
SystemPrepare();
} else {
LogInit();
}
SystemInit();
//启动rcs进程
SystemExecuteRcs();
SystemConfig();
SystemRun();
return 0;
}
这里很明显是将init进程的代码分成了通用的和特有的两部分,共同的代码均在 /base/startup/init/services/init/文件夹下,在这个文件夹下有两个文件夹lite/和standard/分别用来构建小型系统和标准系统的init进程。我们这里主要分析标准进程的启动流程。由于从kernel进程启动的init进程并未传递任何参数,所以会先执行SystemPrepare,而SystemPrepare有两处实现如下:
/base/startup/init/services/init/lite/
H A D init.c 49 void SystemPrepare(void) in SystemPrepare() function
/base/startup/init/services/init/standard/
H A D init.c 223 void SystemPrepare(void) in SystemPrepare() function
我们这里只分析标准系统的流程,所以只看/base/startup/init/services/init/standard/init.c
2.1 SystemPrepare
/base/startup/init/services/init/standard/init.c
void SystemPrepare(void)
{
//挂载一些基本目录并创建一些设备节点
MountBasicFs();
CreateDeviceNode();
LogInit();
// Make sure init log always output to /dev/kmsg.
EnableDevKmsg();
INIT_LOGI("Start init first stage.");
// Only ohos normal system support
// two stages of init.
// If we are in updater mode, only one stage of init.
//检查是否处于升级模式如果没有处于升级模式就进入init第二阶段
if (InUpdaterMode() == 0) {
StartInitSecondStage();
}
}
/base/startup/init/services/utils/init_utils.c
// Check if in updater mode.
int InUpdaterMode(void)
{
#ifdef OHOS_LITE
return 0;
#else
const char * const updaterExecutableFile = "/bin/updater";
if (access(updaterExecutableFile, X_OK) == 0) {
return 1;
} else {
return 0;
}
#endif
}
SystemPrepare主要就是挂载一些基本目录比如/dev,/mnt,/storage,/dev/pts,/proc,/sys,/sys/fs/selinux,接着创建一些设备节点比如/dev/null,/dev/random,/dev/urandom 等等,接着检查系统是否处于升级模式,如果不处于升级模式就启动init的第二阶段。
2.2 StartInitSecondStage
/base/startup/init/services/init/standard/init.c
static void StartInitSecondStage(void)
{
int requiredNum = 0;
//从/proc/cmdline中读取fstab分区表
Fstab *fstab = LoadRequiredFstab();
char **devices = (fstab != NULL) ? GetRequiredDevices(*fstab, &requiredNum) : NULL;
if (devices != NULL && requiredNum > 0) {
//启动Ueventd进程
int ret = StartUeventd(devices, requiredNum);
if (ret == 0) {
//挂载分区
ret = MountRequriedPartitions(fstab);
}
FreeStringVector(devices, requiredNum);
devices = NULL;
ReleaseFstab(fstab);
fstab = NULL;
if (ret < 0) {
// If mount required partitions failure.
// There is no necessary to continue.
// Just abort
INIT_LOGE("Mount required partitions failed; please check fstab file");
// Execute sh for debugging
#ifndef STARTUP_INIT_TEST
execv("/bin/sh", NULL);
abort();
#endif
}
}
if (fstab != NULL) {
ReleaseFstab(fstab);
fstab = NULL;
}
// It will panic if close stdio before execv("/bin/sh", NULL)
CloseStdio();
//启动init进程的第二阶段
INIT_LOGI("Start init second stage.");
SwitchRoot("/usr");
// Execute init second stage
char * const args[] = {
"/bin/init",
"--second-stage",
NULL,
};
//启动init进程并传递参数--second-stage
if (execv("/bin/init", args) != 0) {
INIT_LOGE("Failed to exec \"/bin/init\", err = %d", errno);
exit(-1);
}
}
StartInitSecondStage函数主要就是读取分区表并挂载同时启动Ueventd进程接着再次调用init并传递--second-stage参数。这里的执行execv函数将不会返回,因为调用进程的实体,包括代码段,数据段和堆栈等都已经被新的内容取代,只留下进程ID等一些表面上的信息仍保持原样。只有调用失败了,它们才会返回一个-1,从原程序的调用点接着往下执行。所以接下来的都是init第二阶段的执行过程。再次启动init进程后,当然还是走到了/base/startup/init/services/init/main.c不过不同的是由于携带了--second-stage参数,所以会走到LogInit。接着串行执行SystemInit, SystemExecuteRcs,
SystemConfig和SystemRun。下面我们一个一个来看这些函数都干了点啥。
2.3 LogInit
/base/startup/init/services/init/standard/init.c
void LogInit(void)
{
int ret = mknod("/dev/kmsg", S_IFCHR | S_IWUSR | S_IRUSR,
makedev(MEM_MAJOR, DEV_KMSG_MINOR));
if (ret == 0) {
OpenLogDevice();
}
}
/base/startup/init/services/log/init_log.c
#ifdef INIT_DMESG
static int g_fd = -1;
INIT_LOCAL_API void OpenLogDevice(void)
{
int fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (fd >= 0) {
g_fd = fd;
}
return;
}
可以看到LogInit主要就是创建/dev/kmsg节点并打开设备
2.4 SystemInit
/base/startup/init/services/init/standard/init.c
void SystemInit(void)
{
//初始化信号量
SignalInit();
// Set up a session keyring that all processes will have access to.
KeyCtrlGetKeyringId(KEY_SPEC_SESSION_KEYRING, 1);
//初始化socket /dev/unix/socket
// umask call always succeeds and return the previous mask value which is not needed here
(void)umask(DEFAULT_UMASK_INIT);
MakeDirRecursive("/dev/unix/socket", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
int sock = FdHolderSockInit();
if (sock >= 0) {
RegisterFdHoldWatcher(sock);
}
InitControlFd();
}
系统初始化相关,注册了两个socket第一个是/dev/unix/socket其对应的处理函数为ProcessFdHoldEvent
第二个socket是/dev/unix/socket/init_control_fd对应的处理函数为 ProcessControlFd
2.5 SystemConfig
/base/startup/init/services/init/standard/init.c
void SystemConfig(void)
{
INIT_TIMING_STAT timingStat;
InitSysAdj();
HOOK_EXEC_OPTIONS options;
//init 准备阶段
options.flags = 0;
options.preHook = InitPreHook;
options.postHook = InitPostHook;
InitServiceSpace();
HookMgrExecute(GetBootStageHookMgr(), INIT_GLOBAL_INIT, (void *)&timingStat, (void *)&options);
RecordInitBootEvent("init.prepare");
HookMgrExecute(GetBootStageHookMgr(), INIT_PRE_PARAM_SERVICE, (void *)&timingStat, (void *)&options);
//初始化参数服务,和Android的属性服务一样
InitParamService();
InitParseGroupCfg();
RegisterBootStateChange(BootStateChange);
INIT_LOGI("boot init finish.");
// load SELinux context and policy
// Do not move position!
//加载selinux的上下文和策略
PluginExecCmdByName("loadSelinuxPolicy", "");
RecordInitBootEvent("init.prepare");
RecordInitBootEvent("init.ParseCfg");
//加载特殊的参数
LoadSpecialParam();
// parse parameters
HookMgrExecute(GetBootStageHookMgr(), INIT_PRE_PARAM_LOAD, (void *)&timingStat, (void *)&options);
//加载属性文件
InitLoadParamFiles();
// read config
//读取配置文件
HookMgrExecute(GetBootStageHookMgr(), INIT_PRE_CFG_LOAD, (void *)&timingStat, (void *)&options);
ReadConfig();
RecordInitBootEvent("init.ParseCfg");
INIT_LOGI("boot parse config file done.");
HookMgrExecute(GetBootStageHookMgr(), INIT_POST_CFG_LOAD, (void *)&timingStat, (void *)&options);
IsEnableSandbox();
// execute init
//触发pre-init阶段命令的执行
PostTrigger(EVENT_TRIGGER_BOOT, "pre-init", strlen("pre-init"));
//触发init阶段命令的执行
PostTrigger(EVENT_TRIGGER_BOOT, "init", strlen("init"));
//启动boot阶段的服务
TriggerServices(START_MODE_BOOT);
//触发post-init阶段命令的执行
PostTrigger(EVENT_TRIGGER_BOOT, "post-init", strlen("post-init"));
////启动正常其他的服务
TriggerServices(START_MODE_NORMAL);
clock_gettime(CLOCK_MONOTONIC, &(g_bootJob.startTime));
}
这里的SystemConfig做的工作其实和AOSP源码里init进程解析init.rc并启动各种服务做的工作是一样的,不过多了一步启动参数服务并加载参数。
2.5.1 ParamService
ParamService的初始化是调用InitParamService,代码如下:
/base/startup/init/services/param/linux/param_service.c
void InitParamService(void)
{
PARAM_LOGI("InitParamService pipe: %s.", PIPE_NAME);
CheckAndCreateDir(PIPE_NAME);
CheckAndCreateDir(PARAM_STORAGE_PATH"/");
// param space
PARAM_WORKSPACE_OPS ops = {0};
ops.updaterMode = InUpdaterMode();
// init open log
ops.logFunc = InitLog;
#ifdef PARAM_SUPPORT_SELINUX
ops.setfilecon = setfilecon;
#endif
int ret = InitParamWorkSpace(0, &ops);
PARAM_CHECK(ret == 0, return, "Init parameter workspace fail");
ret = InitPersistParamWorkSpace();
PARAM_CHECK(ret == 0, return, "Init persist parameter workspace fail");
// param server
if (g_paramService.serverTask == NULL) {
ParamStreamInfo info = {};
info.server = PIPE_NAME;
info.close = NULL;
info.recvMessage = NULL;
info.incomingConnect = OnIncomingConnect;
//调用ParamServerCreate创建ParamService
ret = ParamServerCreate(&g_paramService.serverTask, &info);
PARAM_CHECK(ret == 0, return, "Failed to create server");
}
// init trigger space
ret = InitTriggerWorkSpace();
PARAM_CHECK(ret == 0, return, "Failed to init trigger");
RegisterTriggerExec(TRIGGER_PARAM_WAIT, ExecuteWatchTrigger_);
RegisterTriggerExec(TRIGGER_PARAM_WATCH, ExecuteWatchTrigger_);
}
创建一个ParamServer的StreamServerTask并设置给g_paramService的serverTask中客户端的连接会通过le_streamtask.c的HandleServerEvent_调用到param_service.c的OnIncomingConnect接着调用 ProcessMessage去处理请求。
接着是调用LoadSpecialParam从/proc/cmdline加载一些参数,并设置一些build相关的参数
void LoadSpecialParam(void)
{
// read param area size from cfg and save to dac
LoadParamAreaSize();
// read selinux label
LoadSelinuxLabel(NULL);
// from cmdline
LoadParamFromCmdLine();
// from build
LoadParamFromBuild();
}
最后则是从调用InitLoadParamFiles从/system/etc/param文件夹中加载*.para中的属性。
2.5.2 ReadConfig
/base/startup/init/services/init/init_config.c
void ReadConfig(void)
{
// parse cfg
char buffer[32] = {0}; // 32 reason max leb
uint32_t len = sizeof(buffer);
SystemReadParam("ohos.boot.reboot_reason", buffer, &len);
//这里的ohos.boot.reboot_reason是NULL
INIT_LOGV("ohos.boot.reboot_reason %s", buffer);
if (strcmp(buffer, "poweroff_charge") == 0) {
ParseInitCfg(INIT_CONFIGURATION_FILE, NULL);
ReadFileInDir(OTHER_CHARGE_PATH, ".cfg", ParseInitCfg, NULL);
} else if (InUpdaterMode() == 0) {
//正常走这里读取"/etc/init.cfg"
ParseInitCfg(INIT_CONFIGURATION_FILE, NULL);
ParseInitCfgByPriority();
} else {
ReadFileInDir("/etc", ".cfg", ParseInitCfg, NULL);
}
}
static void ParseInitCfgContents(const char *cfgName, const cJSON *root)
{
INIT_ERROR_CHECK(root != NULL, return, "Root is null");
//解析所有的service
ParseAllServices(root);
// parse jobs
//解析所有的jobs,构造触发器
ParseAllJobs(root);
// parse imports
//解析所有导入的配置文件
ParseAllImports(root);
}
int ParseInitCfg(const char *configFile, void *context)
{
UNUSED(context);
INIT_LOGV("Parse init configs from %s", configFile);
//读取文件内容
char *fileBuf = ReadFileToBuf(configFile);
INIT_ERROR_CHECK(fileBuf != NULL, return -1, "Failed to read file content %s", configFile);
//将文件转换成json类
cJSON *fileRoot = cJSON_Parse(fileBuf);
INIT_ERROR_CHECK(fileRoot != NULL, free(fileBuf);
return -1, "Failed to parse json file %s", configFile);
//解析json配置文件
ParseInitCfgContents(configFile, fileRoot);
cJSON_Delete(fileRoot);
free(fileBuf);
return 0;
}
ReadConfig从/etc/init.cfg读取配置信息,init.cfg其实就是android中init.rc的替代品,主要由三部分构成,import,service,jobs
格式大致如下:
"import" : [
"/etc/init.usb.cfg",
"/etc/init.usb.configfs.cfg",
"/vendor/etc/init.${ohos.boot.hardware}.cfg"
],
"jobs" : [{
"name" : "pre-init",
"cmds" : [
....
]
}, {
"name" : "init",
"cmds" : [
....
]
},...
],
"services" : [{
"name" : "usb_service",
"path" : ["/system/bin/sa_main", "/system/profile/usb_service.xml"],
"ondemand" : true,
"uid" : "usb",
"gid" : ["usb", "shell"],
"permission" : [
"ohos.permission.LISTEN_BUNDLE_CHANGE"
],
"apl" : "system_basic",
"secon" : "u:r:usb_service:s0"
}
],
从上面可以看到和android的init.rc是一样的,都是由导入其他配置文件,触发器和服务启动构成。服务大部分都在/etc/init目录下的各种以服务名命名的cfg文件中。其中import主要对应导入其他的配置文件,jobs对应各种触发器,service则是对应需要启动的服务。下面我们一步一步看是如何解析的。
2.5.3 ParseAllServices
ParseAllServices函数主要用来解析/etc/init目录下的各种以服务名命名的cfg文件
/base/startup/init/services/init/init_service_manager.c
void ParseAllServices(const cJSON *fileRoot)
{
int servArrSize = 0;
cJSON *serviceArr = GetArrayItem(fileRoot, &servArrSize, SERVICES_ARR_NAME_IN_JSON);
INIT_CHECK(serviceArr != NULL, return);
INIT_ERROR_CHECK(servArrSize <= MAX_SERVICES_CNT_IN_FILE, return,
"Too many services[cnt %d] detected, should not exceed %d.",
servArrSize, MAX_SERVICES_CNT_IN_FILE);
size_t strLen = 0;
//for循环解析service并添加到NODE_TYPE_SERVICES中的InitGroupNode
for (int i = 0; i < servArrSize; ++i) {
cJSON *curItem = cJSON_GetArrayItem(serviceArr, i);
char *fieldStr = GetStringValue(curItem, "name", &strLen);
if (fieldStr == NULL) {
INIT_LOGE("Failed to get service name");
continue;
}
Service *service = GetServiceByName(fieldStr);
if (service == NULL) {
service = AddService(fieldStr);
if (service == NULL) {
INIT_LOGE("Failed to add service name %s", fieldStr);
continue;
}
} else {
INIT_LOGI("Service %s already exists, updating.", fieldStr);
#ifndef __MUSL__
continue;
#endif
}
service->pid = -1;
//解析服务
int ret = ParseOneService(curItem, service);
if (ret != SERVICE_SUCCESS) {
ReleaseService(service);
service = NULL;
continue;
}
//解析服务中的socket
ret = ParseServiceSocket(curItem, service);
INIT_CHECK(ret == 0, FreeServiceSocket(service->socketCfg); service->socketCfg = NULL);
ret = ParseServiceFile(curItem, service);
INIT_CHECK(ret == 0, FreeServiceFile(service->fileCfg); service->fileCfg = NULL);
// Watch "/dev/console" node for starting console service ondemand.
if ((strcmp(service->name, "console") == 0) && IsOnDemandService(service)) {
if (WatchConsoleDevice(service) < 0) {
INIT_LOGW("Failed to watch \'/dev/console\' device");
}
}
#ifndef OHOS_LITE
/*
* Execute service parsing hooks
*/
ParseServiceHookExecute(fieldStr, curItem);
#endif
ret = GetCmdLinesFromJson(cJSON_GetObjectItem(curItem, "onrestart"), &service->restartArg);
INIT_CHECK(ret == SERVICE_SUCCESS, service->restartArg = NULL);
}
}
ParseAllServices使用for循环调用ParseOneService解析服务的相关参数,调用ParseServiceSocket解析服务中的socket
2.5.4 ParseAllJobs
ParseAllJobs主要是用来解析cfg配置文件中的job标签,job和android的触发器比较相似。
/base/startup/init/services/init/standard/init_jobs.c
void ParseAllJobs(const cJSON *fileRoot)
{
ParseTriggerConfig(fileRoot, CheckJobValid);
}
int ParseTriggerConfig(const cJSON *fileRoot, int (*checkJobValid)(const char *jobName))
{
PARAM_CHECK(fileRoot != NULL, return -1, "Invalid file");
cJSON *triggers = cJSON_GetObjectItemCaseSensitive(fileRoot, TRIGGER_ARR_NAME_IN_JSON);
if (triggers == NULL || !cJSON_IsArray(triggers)) {
return 0;
}
int size = cJSON_GetArraySize(triggers);
PARAM_CHECK(size > 0, return -1, "Trigger array size must positive");
for (int i = 0; i < size && i < TRIGGER_MAX_CMD; ++i) {
cJSON *item = cJSON_GetArrayItem(triggers, i);
//解析Trigger
ParseTrigger_(&g_triggerWorkSpace, item, checkJobValid);
/*
* execute job parsing hooks
*/
ParseJobHookExecute(cJSON_GetStringValue(cJSON_GetObjectItem(item, "name")), item);
}
return 0;
}
/base/startup/init/services/param/trigger/trigger_processor.c
static int ParseTrigger_(const TriggerWorkSpace *workSpace,
const cJSON *triggerItem, int (*checkJobValid)(const char *jobName))
{
PARAM_CHECK(triggerItem != NULL, return -1, "Invalid file");
PARAM_CHECK(workSpace != NULL, return -1, "Failed to create trigger list");
//拿到trigger的name
char *name = cJSON_GetStringValue(cJSON_GetObjectItem(triggerItem, "name"));
PARAM_CHECK(name != NULL, return -1, "Can not get name from cfg");
char *condition = cJSON_GetStringValue(cJSON_GetObjectItem(triggerItem, "condition"));
//从name获取到type
int type = GetTriggerType(name);
PARAM_CHECK(type <= TRIGGER_UNKNOW, return -1, "Failed to get trigger index");
if (type != TRIGGER_BOOT && checkJobValid != NULL && checkJobValid(name) != 0) {
PARAM_LOGI("Trigger %s not exist in group", name);
return 0;
}
TriggerHeader *header = GetTriggerHeader(workSpace, type);
PARAM_CHECK(header != NULL, return -1, "Failed to get header %d", type);
JobNode *trigger = UpdateJobTrigger(workSpace, type, condition, name);
PARAM_CHECK(trigger != NULL, return -1, "Failed to create trigger %s", name);
PARAM_LOGV("ParseTrigger %s type %d count %d", name, type, header->triggerCount);
cJSON *cmdItems = cJSON_GetObjectItem(triggerItem, CMDS_ARR_NAME_IN_JSON);
if (cmdItems == NULL || !cJSON_IsArray(cmdItems)) {
return 0;
}
int cmdLinesCnt = cJSON_GetArraySize(cmdItems);
PARAM_CHECK(cmdLinesCnt > 0, return -1, "Command array size must positive %s", name);
int ret;
int cmdKeyIndex = 0;
//for循环解析命令
for (int i = 0; (i < cmdLinesCnt) && (i < TRIGGER_MAX_CMD); ++i) {
char *cmdLineStr = cJSON_GetStringValue(cJSON_GetArrayItem(cmdItems, i));
PARAM_CHECK(cmdLineStr != NULL, continue, "Command is null");
char *content = NULL;
ret = GetCommandInfo(cmdLineStr, &cmdKeyIndex, &content);
PARAM_CHECK(ret == 0, continue, "Command not support %s", cmdLineStr);
//将命令加入到trigger的命令链表中
ret = AddCommand(trigger, (uint32_t)cmdKeyIndex, content);
PARAM_CHECK(ret == 0, continue, "Failed to add command %s", cmdLineStr);
header->cmdNodeCount++;
}
return 0;
}
同样的for循环调用ParseTrigger_来解析Trigger
2.5.5 ParseAllImports
/base/startup/init/services/init/init_config.c
static void ParseAllImports(const cJSON *root)
{
char *tmpParamValue = calloc(PARAM_VALUE_LEN_MAX + 1, sizeof(char));
INIT_ERROR_CHECK(tmpParamValue != NULL, return, "Failed to alloc memory for param");
cJSON *importAttr = cJSON_GetObjectItemCaseSensitive(root, "import");
if (!cJSON_IsArray(importAttr)) {
free(tmpParamValue);
return;
}
int importAttrSize = cJSON_GetArraySize(importAttr);
for (int i = 0; i < importAttrSize; i++) {
cJSON *importItem = cJSON_GetArrayItem(importAttr, i);
if (!cJSON_IsString(importItem)) {
INIT_LOGE("Invalid type of import item. should be string");
break;
}
char *importContent = cJSON_GetStringValue(importItem);
if (importContent == NULL) {
INIT_LOGE("cannot get import config file");
break;
}
int ret = GetParamValue(importContent, strlen(importContent), tmpParamValue, PARAM_VALUE_LEN_MAX);
if (ret != 0) {
INIT_LOGE("cannot get value for %s", importContent);
continue;
}
INIT_LOGI("Import %s ...", tmpParamValue);
ParseInitCfg(tmpParamValue, NULL);
}
free(tmpParamValue);
return;
}
通过for循环调用ParseInitCfg去解析引入的cfg文件。引入的cfg文件有以下:
[ 4.320616] [pid=1][Init][INFO][init_config.c:78]Import /etc/init.usb.cfg ...
[ 4.321108] [pid=1][Init][INFO][init_config.c:78]Import /vendor/etc/init.rk3568.usb.cfg ...
[ 4.321907] [pid=1][Init][INFO][init_config.c:78]Import /etc/init.usb.configfs.cfg ...
[ 4.322003] [pid=1][Init][INFO][init_config.c:78]Import /vendor/etc/init.rk3568.cfg ...
[ 4.322808] [pid=1][Init][INFO][init_config.c:78]Import init.rk3568.usb.cfg ...
2.5.6 PostTrigger
接着就是调用PostTrigger去触发"pre-init"和"init"阶段的jobs
/base/startup/init/services/param/trigger/trigger_processor.c
void PostTrigger(EventType type, const char *content, uint32_t contentLen)
{
//这里的type = EVENT_TRIGGER_BOOT content分别是"pre-init"和"init“或者"post-init"
PARAM_CHECK(content != NULL && contentLen > 0, return, "Invalid param");
SendTriggerEvent(type, content, contentLen);
}
static void SendTriggerEvent(int type, const char *content, uint32_t contentLen)
{
PARAM_CHECK(content != NULL, return, "Invalid param");
PARAM_LOGV("SendTriggerEvent type %d content %s", type, content);
ParamEventSend(g_triggerWorkSpace.eventHandle, (uint64_t)type, content, contentLen);
}
这里回调用到g_triggerWorkSpace.eventHandle,g_triggerWorkSpace.eventHandle是在初始化ParamService设置成了ProcessBeforeEvent所以最终会调用到
trigger_processor中的ProcessBeforeEvent。
/base/startup/init/services/param/trigger/trigger_processor.c
PARAM_STATIC void ProcessBeforeEvent(const ParamTaskPtr stream,
uint64_t eventId, const uint8_t *content, uint32_t size)
{
PARAM_LOGV("ProcessBeforeEvent %s ", (char *)content);
switch (eventId) {
case EVENT_TRIGGER_PARAM: {
CheckTrigger(&g_triggerWorkSpace, TRIGGER_PARAM,
(const char *)content, size, DoTriggerCheckResult);
ExecuteQueueWork(MAX_TRIGGER_COUNT_RUN_ONCE);
break;
}
//走中间
case EVENT_TRIGGER_BOOT: {
if (g_triggerWorkSpace.bootStateChange != NULL) {
g_triggerWorkSpace.bootStateChange(0, (const char *)content);
}
//DoTriggerCheckResult会将当前的
CheckTrigger(&g_triggerWorkSpace, TRIGGER_BOOT,
(const char *)content, size, DoTriggerCheckResult);
ExecuteQueueWork(MAX_TRIGGER_COUNT_RUN_ONCE);
if (g_triggerWorkSpace.bootStateChange != NULL) {
//这里的bootStateChange主要是统计状态变化使用的时间
g_triggerWorkSpace.bootStateChange(1, (const char *)content);
}
break;
}
case EVENT_TRIGGER_PARAM_WAIT: {
CheckTrigger(&g_triggerWorkSpace, TRIGGER_PARAM_WAIT,
(const char *)content, size, ExecuteTriggerImmediately);
break;
}
case EVENT_TRIGGER_PARAM_WATCH: {
CheckTrigger(&g_triggerWorkSpace, TRIGGER_PARAM_WATCH,
(const char *)content, size, ExecuteTriggerImmediately);
break;
}
default:
break;
}
}
可以看到主要是调用了ExecuteQueueWork
/base/startup/init/services/param/trigger/trigger_processor.c
static void ExecuteQueueWork(uint32_t maxCount)
{
uint32_t executeCount = 0;
//获取到trigger
TriggerNode *trigger = ExecuteQueuePop(&g_triggerWorkSpace);
while (trigger != NULL) {
//执行trigger
StartTriggerExecute_(trigger, NULL, 0);
executeCount++;
if (executeCount > maxCount) {
break;
}
trigger = ExecuteQueuePop(&g_triggerWorkSpace);
}
}
static void StartTriggerExecute_(TriggerNode *trigger, const char *content, uint32_t size)
{
TriggerHeader *triggerHead = GetTriggerHeader(&g_triggerWorkSpace, trigger->type);
if (triggerHead != NULL) {
PARAM_LOGV("StartTriggerExecute_ trigger %s flags:0x%04x",
GetTriggerName(trigger), trigger->flags);
//执行Trigger
triggerHead->executeTrigger(trigger, content, size);
TRIGGER_CLEAR_FLAG(trigger, TRIGGER_FLAGS_QUEUE);
if (TRIGGER_TEST_FLAG(trigger, TRIGGER_FLAGS_SUBTRIGGER)) { // boot && xxx=xxx trigger
const char *condition = triggerHead->getCondition(trigger);
CheckTrigger(&g_triggerWorkSpace, TRIGGER_UNKNOW, condition, strlen(condition), ExecuteTriggerImmediately);
}
if (TRIGGER_TEST_FLAG(trigger, TRIGGER_FLAGS_ONCE)) {
FreeTrigger(&g_triggerWorkSpace, trigger);
}
}
}
这里的executeTrigger也是在trigger_processor初始化时通过RegisterTriggerExec成DoTriggerExecute_,所以最终Trigger的执行调用到了DoTriggerExecute_
如下:
/base/startup/init/services/param/trigger/trigger_processor.c
static int DoTriggerExecute_(const TriggerNode *trigger, const char *content, uint32_t size)
{
PARAM_CHECK(trigger != NULL, return -1, "Invalid trigger");
PARAM_LOGV("Do execute trigger %s type: %d", GetTriggerName(trigger), trigger->type);
PARAM_CHECK(trigger->type <= TRIGGER_UNKNOW, return -1, "Invalid trigger type %d", trigger->type);
CommandNode *cmd = GetNextCmdNode((JobNode *)trigger, NULL);
while (cmd != NULL) {
#ifndef STARTUP_INIT_TEST
//执行命令
DoCmdByIndex(cmd->cmdKeyIndex, cmd->content);
#endif
cmd = GetNextCmdNode((JobNode *)trigger, cmd);
}
return 0;
}
DoTriggerExecute_遍历trigger的命令链表通过调用DoCmdByIndex来执行。
2.5.7 TriggerServices
继续就到了启动服务的流程了首先是启动boot阶段的服务,
/base/startup/init/services/init/standard/init.c
INIT_STATIC void TriggerServices(int startMode)
{
int index = 0;
int jobNum = 0;
char jobName[64] = {0}; // 64 job name
char cmd[64] = {0}; // 64 job name
const int maxServiceInJob = 4; // 4 service in job
//获取到NODE_TYPE_SERVICES中的InitGroupNode
InitGroupNode *node = GetNextGroupNode(NODE_TYPE_SERVICES, NULL);
//通过while循环拿出代表服务的InitGroupNode
while (node != NULL) {
//获取到上面解析的服务
Service *service = node->data.service;
//异常处理
if (service == NULL || service->startMode != startMode) {
node = GetNextGroupNode(NODE_TYPE_SERVICES, node);
continue;
}
if (IsOnDemandService(service)) {
if (CreateServiceSocket(service) != 0) {
INIT_LOGE("service %s exit! create socket failed!", service->name);
}
node = GetNextGroupNode(NODE_TYPE_SERVICES, node);
continue;
}
//组合 start xxx 命令一同来启动服务
if (sprintf_s(cmd, sizeof(cmd), "start %s", service->name) <= 0) {
node = GetNextGroupNode(NODE_TYPE_SERVICES, node);
continue;
}
//构建job的名字
if (index == 0) {
if (sprintf_s(jobName, sizeof(jobName), "boot-service:service-%d-%03d", startMode, jobNum) <= 0) {
node = GetNextGroupNode(NODE_TYPE_SERVICES, node);
continue;
}
jobNum++;
}
index++;
//调用AddCompleteJob通过job机制来调用start xxx来启动服务
AddCompleteJob(jobName, NULL, cmd);
INIT_LOGV("Add %s to job %s", service->name, jobName);
if (index == maxServiceInJob) {
//触发上面的job
PostTrigger(EVENT_TRIGGER_BOOT, jobName, strlen(jobName));
index = 0;
}
node = GetNextGroupNode(NODE_TYPE_SERVICES, node);
}
if (index > 0) {
PostTrigger(EVENT_TRIGGER_BOOT, jobName, strlen(jobName));
}
}
TriggerServices启动服务的大概机制就是创建一个job,在这个job中添加4条命令,每一条命令启动一个服务,然后触发这个job这样就可以一直启动服务了。
每一个job的log如下:
//这里的boot-service:service-2-001 其中的2代表是START_MODE_NORMAL
[ 10.612132] [pid=1][Init][INFO][init.c:270]boot job boot-service:service-2-001 start.
[ 10.612188] [pid=1][Init][INFO][init_service_manager.c:1087]Start service codec_host
[ 10.612961] [pid=1][Init][INFO][init_common_service.c:383]Service codec_host(pid 543) started
[ 10.613187] [pid=1][Init][INFO][init_service_manager.c:1087]Start service light_host
[ 10.613858] [pid=1][Init][INFO][init_common_service.c:383]Service light_host(pid 544) started
[ 10.614059] [pid=1][Init][INFO][init_service_manager.c:1087]Start service vibrator_host
[ 10.614761] [pid=1][Init][INFO][init_common_service.c:383]Service vibrator_host(pid 545) started
[ 10.614957] [pid=1][Init][INFO][init_service_manager.c:1087]Start service sensor_host
[ 10.615849] [pid=1][Init][INFO][init_common_service.c:383]Service sensor_host(pid 546) started
[ 10.616218] [pid=1][Init][INFO][init.c:275]boot job boot-service:service-2-001 finish diff 4068 us.
从Log上看启动的所有服务如下:
[ 4.385955] [pid=1][Init][INFO][init_service_manager.c:1087]Start service ueventd
[ 4.386713] [pid=1][Init][INFO][init_service_manager.c:1087]Start service watchdog_service
[ 7.025390] [pid=1][Init][INFO][init_service_manager.c:1087]Start service hilogd
START_MODE_BOOT阶段启动的服务
[ 7.110652] [pid=1][Init][INFO][init_service_manager.c:1087]Start service hdf_devmgr
[ 7.115731] [pid=1][Init][INFO][init_service_manager.c:1087]Start service hiview
[ 7.116619] [pid=1][Init][INFO][init_service_manager.c:1087]Start service param_watcher
[ 7.117657] [pid=1][Init][INFO][init_service_manager.c:1087]Start service samgr
[ 7.118868] [pid=1][Init][INFO][init_service_manager.c:1087]Start service storage_daemon
[ 7.119847] [pid=1][Init][INFO][init_service_manager.c:1087]Start service device_manager
[ 7.120716] [pid=1][Init][INFO][init_service_manager.c:1087]Start service storage_manager
[ 7.121675] [pid=1][Init][INFO][init_service_manager.c:1087]Start service appspawn
START_MODE_NORMAL阶段启动的服务
[ 7.148924] [pid=1][Init][INFO][init_service_manager.c:1087]Start service storage_daemon
[ 7.151661] [pid=1][Init][INFO][init_service_manager.c:1087]Start service wifi_hal_service
[ 7.154144] [pid=1][Init][INFO][init_service_manager.c:1087]Start service udevd_service
[ 7.155264] [pid=1][Init][INFO][init_service_manager.c:1087]Start service mmi_uinput_service
[ 7.167686] [pid=1][Init][INFO][init_service_manager.c:1087]Start service multimodalinput
[ 9.337235] [pid=1][Init][INFO][init_service_manager.c:1087]Start service device_usage_stats_service
[ 9.342437] [pid=1][Init][INFO][init_service_manager.c:1087]Start service memmgrservice
[ 9.348861] [pid=1][Init][INFO][init_service_manager.c:1087]Start service accessibility
[ 9.356335] [pid=1][Init][INFO][init_service_manager.c:1087]Start service huks_service
[ 9.359630] [pid=1][Init][INFO][init_service_manager.c:1087]Start service devattest_service
[ 9.367228] [pid=1][Init][INFO][init_service_manager.c:1087]Start service bluetooth_service
[ 9.381927] [pid=1][Init][INFO][init_service_manager.c:1087]Start service locationhub
[ 9.409612] [pid=1][Init][INFO][init_service_manager.c:1087]Start service deviceauth_service
[ 9.421050] [pid=1][Init][INFO][init_service_manager.c:1087]Start service resource_schedule_service
[ 9.469054] [pid=1][Init][INFO][init_service_manager.c:1087]Start service pulseaudio
[ 9.573060] [pid=1][Init][INFO][init_service_manager.c:1087]Start service hdf_devhost
[ 9.573181] [pid=1][Init][ERROR][init_service_manager.c:1092]Cannot find service hdf_devhost.
[ 9.587770] [pid=1][Init][INFO][init_service_manager.c:1087]Start service netmanager
[ 9.639384] [pid=1][Init][INFO][init_service_manager.c:1087]Start service wallpaper_service
[ 9.650303] [pid=1][Init][INFO][init_service_manager.c:1087]Start service telephony_sa
[ 9.894095] [pid=1][Init][INFO][init_service_manager.c:1087]Start service ispserver
[ 9.907804] [pid=1][Init][INFO][init_service_manager.c:1087]Start service bgtaskmgr_service
[ 9.921356] [pid=1][Init][INFO][init_service_manager.c:1087]Start service msdp_sa
[ 9.922280] [pid=1][Init][INFO][init_service_manager.c:1087]Start service screenlock_server
[ 9.923239] [pid=1][Init][INFO][init_service_manager.c:1087]Start service edm_sa
[ 10.543689] [pid=1][Init][INFO][init_service_manager.c:1087]Start service time_service
[ 10.577120] [pid=1][Init][INFO][init_service_manager.c:1087]Start service audio_policy
[ 10.596716] [pid=1][Init][INFO][init_service_manager.c:1087]Start service fingerprint_auth_host
[ 10.597655] [pid=1][Init][INFO][init_service_manager.c:1087]Start service user_auth_host
[ 10.598625] [pid=1][Init][INFO][init_service_manager.c:1087]Start service pin_auth_host
[ 10.599478] [pid=1][Init][INFO][init_service_manager.c:1087]Start service face_auth_host
[ 10.612188] [pid=1][Init][INFO][init_service_manager.c:1087]Start service codec_host
[ 10.613187] [pid=1][Init][INFO][init_service_manager.c:1087]Start service light_host
[ 10.614059] [pid=1][Init][INFO][init_service_manager.c:1087]Start service vibrator_host
[ 10.614957] [pid=1][Init][INFO][init_service_manager.c:1087]Start service sensor_host
[ 10.617144] [pid=1][Init][INFO][init_service_manager.c:1087]Start service input_user_host
[ 10.618166] [pid=1][Init][INFO][init_service_manager.c:1087]Start service camera_host
[ 10.619258] [pid=1][Init][INFO][init_service_manager.c:1087]Start service audio_host
[ 10.620246] [pid=1][Init][INFO][init_service_manager.c:1087]Start service wifi_host
[ 10.621669] [pid=1][Init][INFO][init_service_manager.c:1087]Start service power_host
[ 10.629461] [pid=1][Init][INFO][init_service_manager.c:1087]Start service usb_host
[ 10.630509] [pid=1][Init][INFO][init_service_manager.c:1087]Start service blue_host
[ 10.631510] [pid=1][Init][INFO][init_service_manager.c:1087]Start service disp_gralloc_host
[ 10.633054] [pid=1][Init][INFO][init_service_manager.c:1087]Start service privacy_service
[ 10.634105] [pid=1][Init][INFO][init_service_manager.c:1087]Start service distributeddata
[ 10.641969] [pid=1][Init][INFO][init_service_manager.c:1087]Start service installs
[ 10.643163] [pid=1][Init][INFO][init_service_manager.c:1087]Start service inputmethod_service
[ 10.644577] [pid=1][Init][INFO][init_service_manager.c:1087]Start service netsysnative
[ 10.645674] [pid=1][Init][INFO][init_service_manager.c:1087]Start service foundation
[ 10.652420] [pid=1][Init][INFO][init_service_manager.c:1087]Start service sensors
[ 10.653472] [pid=1][Init][INFO][init_service_manager.c:1087]Start service distributedfiledaemon
[ 10.654951] [pid=1][Init][INFO][init_service_manager.c:1087]Start service accountmgr
[ 10.655992] [pid=1][Init][INFO][init_service_manager.c:1087]Start service resource_schedule_service
[ 10.656050] [pid=1][Init][INFO][init_service_manager.c:1087]Start service telephony_sa
[ 10.656096] [pid=1][Init][INFO][init_service_manager.c:1087]Start service media_service
[ 10.657599] [pid=1][Init][INFO][init_service_manager.c:1087]Start service deviceauth_service
[ 10.657643] [pid=1][Init][INFO][init_service_manager.c:1087]Start service av_session
[ 10.665467] [pid=1][Init][INFO][init_service_manager.c:1087]Start service distributedsched
[ 10.666521] [pid=1][Init][INFO][init_service_manager.c:1087]Start service locationhub
[ 10.666869] [pid=1][Init][INFO][init_service_manager.c:1087]Start service ui_service
[ 10.667914] [pid=1][Init][INFO][init_service_manager.c:1087]Start service time_service
[ 10.667969] [pid=1][Init][INFO][init_service_manager.c:1087]Start service edm_sa
[ 10.668005] [pid=1][Init][INFO][init_service_manager.c:1087]Start service useriam
[ 10.676124] [pid=1][Init][INFO][init_service_manager.c:1087]Start service wallpaper_service
[ 10.676173] [pid=1][Init][INFO][init_service_manager.c:1087]Start service screenlock_server
[ 10.676215] [pid=1][Init][INFO][init_service_manager.c:1087]Start service bluetooth_service
[ 10.676247] [pid=1][Init][INFO][init_service_manager.c:1087]Start service camera_service
[ 10.677417] [pid=1][Init][INFO][init_service_manager.c:1087]Start service faultloggerd
[ 10.700828] [pid=1][Init][INFO][init_service_manager.c:1087]Start service devattest_service
[ 10.700881] [pid=1][Init][INFO][init_service_manager.c:1087]Start service netmanager
[ 10.700924] [pid=1][Init][INFO][init_service_manager.c:1087]Start service accessibility
[ 10.701302] [pid=1][Init][INFO][init_service_manager.c:1087]Start service accesstoken_service
[ 10.703147] [pid=1][Init][INFO][init_service_manager.c:1087]Start service memmgrservice
[ 10.703192] [pid=1][Init][INFO][init_service_manager.c:1087]Start service msdp_sa
[ 10.703235] [pid=1][Init][INFO][init_service_manager.c:1087]Start service bgtaskmgr_service
[ 10.703651] [pid=1][Init][INFO][init_service_manager.c:1087]Start service bootanimation
[ 10.719513] [pid=1][Init][INFO][init_service_manager.c:1087]Start service render_service
[ 10.720520] [pid=1][Init][INFO][init_service_manager.c:1087]Start service device_usage_stats_service
[ 10.720583] [pid=1][Init][INFO][init_service_manager.c:1087]Start service wifi_manager_service
[ 10.733451] [pid=1][Init][INFO][init_service_manager.c:1087]Start service hidumper_service
[ 10.734381] [pid=1][Init][INFO][init_service_manager.c:1087]Start service softbus_server
[ 10.735724] [pid=1][Init][INFO][init_service_manager.c:1087]Start service pasteboard_service
[ 10.736756] [pid=1][Init][INFO][init_service_manager.c:1087]Start service ispserver
[ 10.737957] [pid=1][Init][INFO][init_service_manager.c:1087]Start service hdcd
[ 10.859875] [pid=1][Init][INFO][init_service_manager.c:1087]Start service hdcd
[ 11.945313] [pid=1][Init][INFO][init_service_manager.c:1087]Start service usb_service|4201
[ 12.588393] [pid=1][Init][INFO][init_service_manager.c:1087]Start service pinauth
2.6 SystemRun
/base/startup/init/services/init/standard/init.c
void SystemRun(void)
{
StartParamService();
}
/base/startup/init/services/param/linux/param_service.c
int StartParamService(void)
{
// read selinux label
LoadSelinuxLabel("permission");
return ParamServiceStart();
}
可以看到SystemRun主要就是启动ParamService
三、服务的启动框架
在上面的2.5.3小节我们说过ParseAllServices函数主要用来解析/etc/init目录下的各种以服务名命名的cfg文件,解析完毕后会将服务的信息存储在NODE_TYPE_SERVICES中的InitGroupNode接着在2.5.7小节的TriggerServices去启动它,TriggerServices后续的流程图如下:
这里我们以CameraService为例介绍一下OpenHarmony启动服务的基本流程,
首先camera_service的cfg文件内容如下:
/foundation/multimedia/camera_framework/services/etc/camera_service.cfg
{
"services" : [{
"name" : "camera_service",
"path" : ["/system/bin/sa_main", "/system/profile/camera_service.xml"],
"uid" : "cameraserver",
"gid" : ["system", "shell"],
"secon" : "u:r:camera_service:s0",
"permission" : ["ohos.permission.GET_SENSITIVE_PERMISSIONS", "ohos.permission.PERMISSION_USED_STATS"],
"permission_acls" : ["ohos.permission.GET_SENSITIVE_PERMISSIONS"]
}
]
}
可以看到他是通过执行system/bin/sa_main程序导入了/system/profile/camera_service.xml配置文件启动的camera_service服务就和Android的init.rc中启动native进程是类似的。接着我们看一下camera_service.xml
<!--设备里的/system/profile/camera_service.xml-->
<?xml version="1.0" encoding="utf-8"?>
<info>
<process>camera_service</process>
<loadlibs>
<libpath>libcamera_service.z.so</libpath>
</loadlibs>
<systemability>
<name>3008</name>
<libpath>libcamera_service.z.so</libpath>
<run-on-create>true</run-on-create>
<distributed>false</distributed>
<dump-level>1</dump-level>
</systemability>
</info>
服务的启动流程首先要先知道sa_main可执行程序的入口在哪里,在代码库中搜索sa_main 并在filePath中输入gn即可搜到sa_main的代码路径如下:
/foundation/systemabilitymgr/safwk/services/safwk/src/
3.1 sa.main
入口函数是main.cpp
/foundation/systemabilitymgr/safwk/services/safwk/src/main.cpp
int main(int argc, char *argv[]){
//我们这里一般的服务只有一个参数 就是
HILOGI(TAG, "[PerformanceTest] SAFWK main entry process starting!");
//回调接口
auto setProcessName = [argc, argv](const string& name) -> void {
uintptr_t start = reinterpret_cast<uintptr_t>(argv[0]);
uintptr_t end = reinterpret_cast<uintptr_t>(strchr(argv[argc - 1], 0));
uintptr_t argvSize = end - start;
if (memset_s(argv[0], argvSize, 0, argvSize) != EOK) {
HILOGW(TAG, "failed to clear argv:%s", strerror(errno));
return;
}
if (strcpy_s(argv[0], argvSize, name.c_str()) != EOK) {
HILOGW(TAG, "failed to set process name:%s", strerror(errno));
return;
}
HILOGI(TAG, "Set process name to %{public}s", argv[0]);
};
// Load ondemand system abilities related shared libraries from specific xml-format profile
// when this process starts.
//初始saId为-1
int32_t saId = DEFAULT_SAID;
if (argc > ONDEMAND_LOAD) {
//从参数中解析saId
saId = ParseSaId(argv);
if (!CheckSaId(saId)) {
HILOGE(TAG, "saId is invalid!");
return 0;
}
}
// Load default system abilities related shared libraries from specific xml-format profile,
// when this process starts.
//解析服务的profile文件就是camera_service.xml
string profilePath(DEFAULT_XML);
if (argc > DEFAULT_LOAD) {
string filePath(argv[PROFILE_INDEX]);
if (filePath.empty() || filePath.find(".xml") == string::npos) {
HILOGE(TAG, "profile file path is invalid!");
return 0;
}
//调用SetProcName设置进程的名字
SetProcName(filePath, setProcessName);
profilePath = std::move(filePath);
}
//调用DoStartSAProcess启动服务
LocalAbilityManager::GetInstance().DoStartSAProcess(profilePath, saId);
return 0;
}
3.2 LocalAbilityManager.DoStartSAProcess
可以看到sa_main的main函数主要是设置进程名字接着调用LocalAbilityManager的DoStartSAProcess来启动服务
/foundation/systemabilitymgr/safwk/services/safwk/src/local_ability_manager.cpp
void LocalAbilityManager::DoStartSAProcess(const std::string& profilePath, int32_t saId)
{
HILOGI(TAG, "DoStartSAProcess saId : %d", saId);
string realProfilePath = "";
if (!CheckAndGetProfilePath(profilePath, realProfilePath)) {
HILOGE(TAG, "DoStartSAProcess invalid path");
return;
}
{
std::string traceTag = GetTraceTag(realProfilePath);
HITRACE_METER_NAME(HITRACE_TAG_SAMGR, traceTag);
//InitSystemAbilityProfiles 解析进程名称并打开对应的so文件注册相应的ability
bool ret = InitSystemAbilityProfiles(realProfilePath, saId);
if (!ret) {
HILOGE(TAG, "InitSystemAbilityProfiles no right profile, will exit");
return;
}
//等待SystemAbilityManager启动 SystemAbilityManager和Android的ServiceManager功能类似主要负责系统能力的添加查找
//最多等待10s
ret = CheckSystemAbilityManagerReady();
if (!ret) {
HILOGE(TAG, "CheckSystemAbilityManagerReady failed! will exit");
return;
}
//将注册的ability加入到abilityPhaseMap_中的saList中
ret = InitializeSaProfiles(saId);
if (!ret) {
HILOGE(TAG, "InitializeSaProfiles failed! will exit");
return;
}
//启动ability
ret = Run(saId);
if (!ret) {
HILOGE(TAG, "Run failed! will exit");
return;
}
}
IPCSkeleton::JoinWorkThread();
ClearResource();
HILOGE(TAG, "JoinWorkThread stop, will exit");
}
DoStartSAProcess主要做了三件事情
1.调用InitSystemAbilityProfiles解析进程名称并打开和加载对应的so文件
2.等待SystemAbilityManager启动
3.将解析出来的ability加入到saList中
4.启动ability
3.3 注册Ability
其中第一条在加载so文件时会加载服务的类这时会调用到如下函数:
/foundation/multimedia/camera_framework/services/camera_service/src/hcamera_service.cpp
REGISTER_SYSTEM_ABILITY_BY_ID(HCameraService, CAMERA_SERVICE_ID, true)
/foundation/systemabilitymgr/safwk/services/safwk/include/system_ability.h
#define REGISTER_SYSTEM_ABILITY_BY_ID(abilityClassName, systemAbilityId, runOnCreate) \
const bool abilityClassName##_##RegisterResult = \
SystemAbility::MakeAndRegisterAbility(new abilityClassName(systemAbilityId, runOnCreate));
意思就是将HCameraService注册成为CAMERA_SERVICE_ID的systemability,可以看到最终调用到了 SystemAbility::MakeAndRegisterAbility
/foundation/systemabilitymgr/safwk/services/safwk/src/system_ability.cpp
bool SystemAbility::MakeAndRegisterAbility(SystemAbility* systemAbility)
{
HILOGD(TAG, "registering system ability...");
return LocalAbilityManager::GetInstance().AddAbility(systemAbility);
}
/foundation/systemabilitymgr/safwk/services/safwk/src/local_ability_manager.cpp
bool LocalAbilityManager::AddAbility(SystemAbility* ability)
{
if (ability == nullptr) {
HILOGW(TAG, "try to add null ability!");
return false;
}
int32_t saId = ability->GetSystemAbilitId();
SaProfile saProfile;
bool ret = profileParser_->GetProfile(saId, saProfile);
if (!ret) {
return false;
}
std::unique_lock<std::shared_mutex> writeLock(abilityMapLock_);
auto iter = abilityMap_.find(saId);
if (iter != abilityMap_.end()) {
HILOGW(TAG, "try to add existed ability:%{public}d!", saId);
return false;
}
HILOGI(TAG, "set profile attributes for SA:%{public}d", saId);
ability->SetLibPath(saProfile.libPath);
ability->SetRunOnCreate(saProfile.runOnCreate);
ability->SetDependSa(saProfile.dependSa);
ability->SetDependTimeout(saProfile.dependTimeout);
ability->SetDistributed(saProfile.distributed);
ability->SetDumpLevel(saProfile.dumpLevel);
ability->SetCapability(saProfile.capability);
ability->SetPermission(saProfile.permission);
abilityMap_.emplace(saId, ability);
return true;
}
可以看到最终将ability加入到了abilityMap_中
3.4 启动ability
在3.2小节中启动ability走的时run函数
/foundation/systemabilitymgr/safwk/services/safwk/src/local_ability_manager.cpp
bool LocalAbilityManager::Run(int32_t saId)
{
HILOGD(TAG, "local ability manager is running...");
//把当前的LocalAbilityManager和processName注册给SystemAbilityManager
bool addResult = AddLocalAbilityManager();
if (!addResult) {
HILOGE(TAG, "failed to add local abilitymanager");
return false;
}
HILOGD(TAG, "success to add process name:%{public}s", Str16ToStr8(procName_).c_str());
uint32_t concurrentThreads = std::thread::hardware_concurrency();
HILOGI(TAG, "concurrentThreads is %{public}d", concurrentThreads);
initPool_->Start(concurrentThreads);
initPool_->SetMaxTaskNum(MAX_TASK_NUMBER);
//启动ability
FindAndStartPhaseTasks();
RegisterOnDemandSystemAbility(saId);
initPool_->Stop();
return true;
}
void LocalAbilityManager::FindAndStartPhaseTasks()
{
std::shared_lock<std::shared_mutex> readLock(abilityMapLock_);
for (uint32_t startType = BOOT_START; startType <= OTHER_START; ++startType) {
//将abilityPhaseMap_中的ability全部启动
auto iter = abilityPhaseMap_.find(startType);
if (iter != abilityPhaseMap_.end()) {
StartPhaseTasks(iter->second);
}
}
}
void LocalAbilityManager::StartPhaseTasks(const std::list<SystemAbility*>& systemAbilityList)
{
if (systemAbilityList.empty()) {
return;
}
for (auto systemAbility : systemAbilityList) {
if (systemAbility != nullptr) {
HILOGD(TAG, "add phase task for SA:%{public}d", systemAbility->GetSystemAbilitId());
std::lock_guard<std::mutex> autoLock(startPhaseLock_);
++startTaskNum_;
//使用initPool_调用StartSystemAbilityTask启动systemAbility
auto task = std::bind(&LocalAbilityManager::StartSystemAbilityTask, this, systemAbility);
initPool_->AddTask(task);
}
}
int64_t begin = GetTickCount();
HILOGD(TAG, "start waiting for all tasks!");
std::unique_lock<std::mutex> lck(startPhaseLock_);
if (!startPhaseCV_.wait_for(lck, std::chrono::seconds(MAX_SA_STARTUP_TIME),
[this] () { return startTaskNum_ == 0; })) {
HILOGW(TAG, "start timeout!");
}
startTaskNum_ = 0;
int64_t end = GetTickCount();
HILOGI(TAG, "start tasks finished and spend %{public}" PRId64 " ms", (end - begin));
}
void LocalAbilityManager::StartSystemAbilityTask(SystemAbility* ability)
{
if (ability != nullptr) {
HILOGD(TAG, "StartSystemAbility is called for %{public}d", ability->GetSystemAbilitId());
//如果ability不依赖其他sa那么直接启动
if (ability->GetDependSa().empty()) {
ability->Start();
} else {
int64_t start = GetTickCount();
int64_t dependTimeout = ability->GetDependTimeout();
while (!CheckDependencyStatus(ability->GetDependSa()).empty()) {
int64_t end = GetTickCount();
int64_t duration = ((end >= start) ? (end - start) : (INT64_MAX - end + start));
if (duration < dependTimeout) {
usleep(CHECK_DEPENDENT_SA_PERIOD);
} else {
break;
}
}
vector<u16string> unpreparedDeps = CheckDependencyStatus(ability->GetDependSa());
if (unpreparedDeps.empty()) {
ability->Start();
} else {
for (const auto& unpreparedDep : unpreparedDeps) {
HILOGI(TAG, "%{public}d's dependency:%{public}s not started in %{public}d ms",
ability->GetSystemAbilitId(), Str16ToStr8(unpreparedDep).c_str(), ability->GetDependTimeout());
}
}
}
}
std::lock_guard<std::mutex> lock(startPhaseLock_);
if (startTaskNum_ > 0) {
--startTaskNum_;
}
startPhaseCV_.notify_one();
}
从上面代码我们可以知道服务的启动最终调用到了SystemAbility的start函数,由于我们注册的时候注册的是HCameraService,所以会调用到HCameraService的start函数。最终会调用到HCameraService的onStart函数,至此服务就被启动起来了。不同的服务只是需要加载的so库不一样注册的服务不一样,用的都是这一套框架。
标签:INFO,Openharmony,manager,service,启动,流程,pid,Init,init From: https://blog.51cto.com/u_16071150/7322675