首页 > 其他分享 >Openharmony的启动流程

Openharmony的启动流程

时间:2023-09-01 15:06:10浏览次数:42  
标签:INFO Openharmony manager service 启动 流程 pid Init init

OpenHarmony启动流程

OpenHarmony 源码网站:

http://ci.openharmony.cn/codeSearch

https://www.lengqinjie.xyz/lxr/source/

一、kernel的启动

流程图:

Openharmony的启动流程_初始化

由于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后续的流程图如下:

Openharmony的启动流程_linux_02

这里我们以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

相关文章

  • WPF 动态更改启动窗体startupUri
    第一步:在App.xaml里,把StartupUri=""去掉,改成Startup="Application_Startup"第二步:在App.xaml.cs里,增加Application_Startup事件:privatevoidApplication_Startup(objectsender,StartupEventArgse){ApplicationcurrApp=Application.Current;currAp......
  • 解决Activity启动黑屏及设置android:windowIsTranslucent不兼容activity切换动画问题
    注:如果设置和取消会造成不同bug,冲突的解决设置不同的style,具体另行百度。。。。。。。之前在做APP的时候不太关注这个问题,因为自己在使用其他APP的时候也会在应用启动的初始有一个黑屏闪过后才会出现应用的欢迎页。直到最近开发过程中发现自己在欢迎页启动的线程由于请求和......
  • APP上架需要的准备和流程
    一上架iOS应用市场前的准备1.选择适合自己的苹果开发者账号(1)个人账号(Individual):费用99美金一年,该账号在AppStore销售者只能显示个人的ID,比如zhitianzhang,单人使用。个人账号只能有一个开发者。100个苹果的iOS设备UDID测试。(2)公司/机构账号(Company/Organization):费......
  • 2022年iOS上架及证书最新申请流程
    最近的15年,手机行业无论怎么变,ios系统依然还是占据着行业的榜首位置,而打包一个苹果的app,门槛则比较高。主要的原因在于苹果app的开发,打包时需要p12格式的证书文件和描述文件profile文件(在hbuilder和apicloud这些h5打包平台,ios证书又叫私钥证书。),而这些文件的创建则又需要苹果mac电......
  • App Store上架iOS App的详细流程
    最近负责将公司某个项目的iOS应用上架到AppStore,在上架的过程中,需要做的事情很多,但很多都不涉及到太多的技术问题,但是流程很多,而且对应用有很多要求,在上架的过程中也遇到一些问题,后也顺利解决问题,成功上架了。我觉得有必要记录一下过程及遇到的问题,希望对有需要上架的iOS开发者们......
  • iOS 最新 APP提交上架流程~(打包 ipa)
    文章目录苹果开发者账号个人开发者账号公司开发者账号:企业开发者账号:1、申请APPID2、申请邓白氏码(个人开发者账号不需要)3、等待邓白氏申请成功4、开发者账号申请5、申请过程中实名认证出错随着苹果2020年对开发者账号申请流程变更,2020年之前的申请开发者账号流程已经成为过去时,现在......
  • iOS-申请开发证书流程
    1、开发者证书(分为开发和发布两种,类型为iosDevelopment,iosDistribution),这个是最基础的,不论是真机调试,还是上传到appstore都是需要的,是一个基证书,用来证明自己开发者身份的;2、appID,这是每一个应用的独立标识,在设置项中可以配置该应用的权限,比如是否用到了PassBook,GameCenter,以......
  • iOS开发之进阶篇(1)—— 证书、打包上架流程、p12文件
    iOS开发相关的证书有很多种,iOS开发证书(iOSAppDevelopment)、iOS发布证书(iOSDistribution(AppStoreandAdHoc))、网站推送ID证书(WebsitePushIDCertificate)、ApplePay付款处理证书(ApplePayPaymentProcessingCertificate)等等。Xcode11以后,又新增了AppleDevelopment和Ap......
  • app上架苹果市场流程
    iOS上架的流程主要可以简单总结为:一个包,两个网址,三个证书,一个包:iPA包,上架用的.两个网址:1>https://itunesconnect.apple.com2>https://developer.apple.com三个证书:1>签名证书.(MAC电脑钥匙串生成)2>制作发布证书.(签名证书+开发者账号)3>制作PP配置文件.(发布证书+A......
  • 可视化流程设计平台有啥优势?
    在流程化办公发展趋势逐渐明朗的今天,运用什么样的平台可以帮助广大用户朋友实现这一目标?可视化流程设计平台是轻量级、更灵活、易操作、效率高的平台,可以快速定制客户专属的框架平台,为每一位客户朋友做好数据管理,实现高效率、高质量的办公。如果需要做好数据管理,需要提高办公协作......