想了解更多内容,启动请访问: 和华为官方合作共建的流程鸿蒙技术社区 https://harmonyos.51cto.com 内核启动完是init阶段,源码的分析路径在 base\startup\init_lite\services\src\main.c,虽然文件夹命名为init_lite,启动但是流程init部分的代码是小型系统(small system)和标准系统(standard system)通用的。相关码仓启动模块init进程 (gitee.com) 接下来参考linux内核梳理下启动流程,分析 /dev/null,启动空设备,流程特殊的分析设备文件,丢弃一切写入其中的启动数据(但报告写入操作成功),读取它则会立即得到一个EOF。流程其作用是分析对stdin/stdout/stderr进行保护,把文件描述符0,启动1,流程2分配出去,分析以后再分配的时候就不会将stdin/stdout/stderr打开,以达到保护目的。 写入/dev/kmsg的信息,可以在dmesg(开机信息)中查看。 标准系统空实现,不深究了。服务器租用 注释很清楚了,printk_devkmsg默认是流控的,设置为on取消流控。 /dev/unix/socket/不过这个干啥用的,还没搞清楚。 兼容常规的*.rc文件,是执行linux运行命令的脚本文件。 OHOS3.0中的命令脚本文件是*.cfg,采用JSON格式,存储的信息更多一些。前面都是启动系统的一些准备工作,而接下来才是重要部分。还是用个表格来分析。 base\startup\init_lite\services\src\init_read_cfg.c 编译框架使用的哪个init.cfg还有待确定。 到这里init.cfg就解析并执行完毕了,需要注意的是,import导入的cfg文件是最后才执行的源码下载。 和import的原理类似,遍历system/etc/init文件夹下的*.cfg文件,并执行。我感觉写在init.cfg的import中应该也是可以的。 PostTrigger(EVENT_BOOT, “pre-init”, strlen(“pre-init”)); 使用Libuv库,官网地址。作为Nodejs的底层。 相关的API可以参考网址,uv_run。 至此,init启动结束,进入pause()。总结下来init首先会执行一些通用的准备操作,同时兼容常规linux内核启动脚本,之后在执行鸿蒙init.cfg和单板相关*.cfg。以上分析都是我个人见解,如有错误欢迎指正。 想了解更多内容,请访问: 和华为官方合作共建的鸿蒙技术社区 https://harmonyos.51cto.cominit阶段
1.关闭输入输出
2.在串口打印调试信息
3.挂载目录,建立索引节点
// base\startup\init_lite\services\src\device.c void MountBasicFs(void) { mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755" mkdir("/dev/pts", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) mount("devpts", "/dev/pts", "devpts", 0, NULL) mount("proc", "/proc", "proc", 0, "hidepid=2") mount("sysfs", "/sys", "sysfs", 0, NULL) mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL) } void CreateDeviceNode(void) { mknod("/dev/kmsg", S_IFCHR | DEFAULT_NO_AUTHORITY_MODE, makedev(MEM_MAJOR, DEV_KMSG_MINOR) mknod("/dev/null", S_IFCHR | DEFAULT_RW_MODE, makedev(MEM_MAJOR, DEV_NULL_MINOR) mknod("/dev/random", S_IFCHR | DEFAULT_RW_MODE, makedev(MEM_MAJOR, DEV_RANDOM_MINOR) mknod("/dev/urandom", S_IFCHR | DEFAULT_RW_MODE, makedev(MEM_MAJOR, DEV_URANDOM_MINOR) } 4.开启DevKmsg
5.建立Socket文件夹
6.Singnal初始化
// base\startup\init_lite\services\src\init_signal_handler.c void SignalInitModule() { int ret = uv_signal_init(uv_default_loop(), &g_sigchldHandler); int ret1 = uv_signal_init(uv_default_loop(), &g_sigtermHandler); if (ret != 0 && ret1 != 0) { INIT_LOGW("initialize signal handler failed"); return; } if (uv_signal_start(&g_sigchldHandler, UVSignalHandler, SIGCHLD) != 0) { INIT_LOGW("start SIGCHLD handler failed"); } if (uv_signal_start(&g_sigtermHandler, UVSignalHandler, SIGTERM) != 0) { INIT_LOGW("start SIGTERM handler failed"); } } 7.执行命令脚本文件
8.【重要】解析并执行*.cfg文件。
a.初始化服务参数的工作区
// base\startup\init_lite\services\param\service\param_service.c void InitParamService() { int ret = InitParamWorkSpace(&g_paramWorkSpace, 0, g_initContext); PARAM_CHECK(ret == 0, return, "Init parameter workspace fail"); } b.解析init.cfg文件
./base/startup/init_lite/services/etc/init.cfg ./base/update/updater/services/etc/init.cfg ./out/ohos-arm-release/packages/phone/system/etc/init.cfg ./out/ohos-arm-release/packages/phone/updater/etc/init.cfg ./out/ohos-arm-release/obj/base/startup/init_lite/services/base/startup/init_lite/services/etc/init.cfg ./out/ohos-arm-release/obj/base/update/updater/services/base/update/updater/services/etc/init.cfg ./device/hisilicon/hi3516dv300/updater/init.cfg ①.解析"services"部分,并执行
// base\startup\init_lite\services\src\init_service_manager.c void ParseAllServices(const cJSON* fileRoot) { int servArrSize = 0; cJSON* serviceArr = GetArrItem(fileRoot, &servArrSize, "services"); ... // 截取 init.cfg 中的"services"部分 ///////////////////////////////// 参考格式 "services" : [{ "name" : "ueventd", "path" : ["/system/bin/ueventd"], "critical" : 1 }, { "name" : "console", "path" : ["/system/bin/sh"], "disabled" : 1, "console" : 1, "uid" : "root", "gid" : ["shell", "log", "readproc"] }] ///////////////////////////////// 参考格式 ... // 默认服务数量不能超过100 Service* retServices = (Service*)realloc(g_services, sizeof(Service) * (g_servicesCnt + servArrSize)); ... // Skip already saved services, Service* tmp = retServices + g_servicesCnt; if (memset_s(tmp, sizeof(Service) * servArrSize, 0, sizeof(Service) * servArrSize) != EOK) { free(retServices); retServices = NULL; return; } // 【重要】然后使用一个for循环遍历服务数组 for (int i = 0; i < servArrSize; ++i) { cJSON* curItem = cJSON_GetArrayItem(serviceArr, i); if (CheckServiceKeyName(curItem) != SERVICE_SUCCESS) { ReleaseServiceMem(&tmp[i]); tmp[i].attribute |= SERVICE_ATTR_INVALID; continue; } int ret = ParseOneService(curItem, &tmp[i]); if (ret != SERVICE_SUCCESS) { // 如果服务启动失败 // release resources if it fails ReleaseServiceMem(&tmp[i]); tmp[i].attribute |= SERVICE_ATTR_INVALID; INIT_LOGE("Parse information for service %s failed. ", tmp[i].name); continue; } else { // 如果服务启动成功 INIT_LOGD("service[%d] name=%s, uid=%d, critical=%d, disabled=%d", i, tmp[i].name, tmp[i].servPerm.uID, (tmp[i].attribute & SERVICE_ATTR_CRITICAL) ? 1 : 0, (tmp[i].attribute & SERVICE_ATTR_DISABLED) ? 1 : 0); } if (GetServiceSocket(curItem, &tmp[i]) != SERVICE_SUCCESS) { if (tmp[i].socketCfg != NULL) { FreeServiceSocket(tmp[i].socketCfg); tmp[i].socketCfg = NULL; } } if (GetServiceOnRestart(curItem, &tmp[i]) == SERVICE_FAILURE) { INIT_LOGE("Failed Get Service OnRestart service"); } } // Increase service counter. RegisterServices(retServices, servArrSize); // 最后注册服务 } ---------------------------------------------------------------------- void RegisterServices(Service* services, int servicesCnt) { if (services == NULL) { return; } g_services = services; g_servicesCnt += servicesCnt; // 到这里init.cfg中的"services"部分就已经解析并执行完毕了。 } ②.解析"jobs"部分,并执行
// base\startup\init_lite\services\param\trigger\trigger_processor.c int ParseTriggerConfig(const cJSON *fileRoot) { ... int ret = InitTriggerWorkSpace(&g_triggerWorkSpace); // 初始化触发器的工作空间 ... cJSON *triggers = cJSON_GetObjectItemCaseSensitive(fileRoot, "jobs"); // 提取init.cfg中"jobs"的部分 ... int size = cJSON_GetArraySize(triggers); ... for (int i = 0; i < size; ++i) { cJSON *item = cJSON_GetArrayItem(triggers, i); ParseTrigger(&g_triggerWorkSpace, item); } return 0; } ---------------------------------------------------------------------- // 截取init.cfg中jobs段的部分代码,格式如下 "jobs" : [{ "name" : "pre-init", "cmds" : [ "write /proc/sys/kernel/sysrq 0", ... "mkdir /data", ] }, { "name" : "init", "cmds" : [ "copy /proc/cmdline /dev/urandom", ... "domainname localdomain" ] }, { "name" : "param:sys.boot_from_charger_mode=1", "condition" : "sys.boot_from_charger_mode=1", "cmds" : [ "trigger post-init" ] }, ... ], // base\startup\init_lite\services\param\trigger\trigger_manager.c int ParseTrigger(TriggerWorkSpace *workSpace, const cJSON *triggerItem) { ... // 提取init.cfg中jobs段的"name"的部分 char *name = cJSON_GetStringValue(cJSON_GetObjectItem(triggerItem, "name")); ... // 提取init.cfg中jobs段的"condition"的部分 char *condition = cJSON_GetStringValue(cJSON_GetObjectItem(triggerItem, "condition")); int index = GetTriggerIndex(name); ... u_int32_t offset = 0; TriggerNode *trigger = GetTriggerByName(workSpace, name, &offset); if (trigger == NULL) { offset = AddTrigger(workSpace, index, name, condition); PARAM_CHECK(offset > 0, return -1, "Failed to create trigger %s", name); trigger = GetTriggerByIndex(workSpace, offset); } else { if (condition != NULL) { PARAM_LOGE("Warning parseTrigger %s %s", name, condition); } } PARAM_LOGD("ParseTrigger %s %u", name, offset); // 添加命令行 cJSON* cmdItems = cJSON_GetObjectItem(triggerItem, "cmds"); // 提取init.cfg中jobs段的"cmds"的部分 ... int cmdLinesCnt = cJSON_GetArraySize(cmdItems); // 获取命令数量 ... for (int i = 0; i < cmdLinesCnt; ++i) { // 循环执行 char *cmdLineStr = cJSON_GetStringValue(cJSON_GetArrayItem(cmdItems, i)); ... size_t cmdLineLen = strlen(cmdLineStr); const char *matchCmd = GetMatchCmd(cmdLineStr); if (matchCmd == NULL && strncmp(cmdLineStr, "trigger ", strlen("trigger ")) == 0) { matchCmd = "trigger "; } ... size_t matchLen = strlen(matchCmd); if (matchLen == cmdLineLen) { offset = AddCommand(workSpace, trigger, matchCmd, NULL); } else { offset = AddCommand(workSpace, trigger, matchCmd, cmdLineStr + matchLen); } PARAM_CHECK(offset > 0, continue, "Failed to add command %s", cmdLineStr); } return 0; } ③.提取init.cfg中"import"的部分,并执行
// base\startup\init_lite\services\src\init_import.c void ParseAllImports(const cJSON *root) { ///////////////////////////////// 参考格式 "import" : [ "/etc/init.usb.cfg", "/etc/init.usb.configfs.cfg", "/etc/init.usb.cfg", "/etc/init.Hi3516DV300.usb.cfg", "/etc/init.Hi3516DV300.cfg" ], ///////////////////////////////// cJSON *importAttr = cJSON_GetObjectItemCaseSensitive(root, "import"); // 提取init.cfg中"import"的部分 ... int importAttrSize = cJSON_GetArraySize(importAttr); for (int i = 0; i < importAttrSize; i++) { // 循环取出每一项 cJSON *importItem = cJSON_GetArrayItem(importAttr, i); ... char *importContent = cJSON_GetStringValue(importItem); ... // Only OHOS L2 support parameter. #ifndef OHOS_LITE //这里有啥意义,前面都已经判断过了 if (ExtractCfgFile(&cfgFile, importContent) < 0) { INIT_LOGW("Failed to import from %s", importContent); if (cfgFile != NULL) { free(cfgFile); cfgFile = NULL; } continue; } #else cfgFile = importContent; #endif INIT_LOGI("Import %s...", cfgFile); ParseInitCfg(cfgFile); // 取出"import"中的路径,解析方法和init.cfg解析方式一致。 ... } INIT_LOGD("parse import file done"); return; } c.解析执行/system/etc/init/*.cfg文件
d.PostTrigger
9.启动参数服务