OpenHarmony-1.启动流程

  • OpenHarmony启动流程

1.OpenHarmony 标准系统启动引导流程

  OpenHarmony标准系统默认支持以下几个镜像:
在这里插入图片描述

  每个开发板都需要在存储器上划分好分区来存放上述镜像,SOC启动时都由bootloader来加载这些镜像,具体过程包括以下几个大的步骤:

  • bootloader 初始化ROM和RAM等硬件,加载分区表信息。
  • bootloader 根据分区表加载boot.img,从中解析并加载ramdisk.img到内存中。
  • bootloader 准备好分区表信息,ramdisk地址等信息,进入内核,内核加载ramdisk并执行init。
  • init 准备初始文件系统,挂载required.fstab(包括system.img和vendor.img的挂载)。
  • 扫描 system.img和vendor.img中etc/init目录下的启动配置脚本,执行各个启动命令。

1.1.u-boot

  u-boot启动进入内核时,可以通过bootargs传递关键信息给内核,这一部分内容是与平台相关的,主要信息如下:
在这里插入图片描述

1.2.kernel 启动

流程图如下所示:
在这里插入图片描述  OpenHarmony(简称OH)的标准系统的底层系统是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.*/pid = kernel_thread(kernel_init, NULL, CLONE_FS);...
}
static int __ref kernel_init(void *unused)
{...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内核开发的,所以启动init进程,那么OH的init进程的入口为/base/startup/init/services/init/main.c中。

1.3.init 进程
在这里插入图片描述

  • 基础环境初始化:init 进程挂载 tmpfs 和 procfs,创建基本的 dev 设备节点,提供一个基本的根文件系统。

    • tmpfs:这是一个内存上的文件系统,用于存储临时数据,比如在启动期间创建的目录、缓存等。它不会持久化到磁盘,当系统重启时会自动清除。
    • procfs:这个文件系统提供内核运行时信息的接口,如进程列表、系统配置、硬件状态等。init进程通常会挂载procfs,以便在启动早期获取和管理这些信息。
  • 从/proc/cmdline中读取fstab分区表。

  • 热插拔事件监听:init 进程启动 ueventd 来监控内核中的设备热插拔事件,为新插入的 block 设备分区(如 system和 vendor 分区)创建相应的 dev 设备节点。当设备被插入或移除时,内核会通过 uevent(用户空间事件)机制发送消息给 ueventd。ueventd作为系统服务的一部分,负责监听这些netlink事件,并根据接收到的事件类型动态管理相应的设备节点。

  • 服务启动:挂载完分区后,init 会扫描 /system/etc/init 目录下的 init.cfg(base/startup/init/services/etc/init.cfg) 文件,根据配置启动各个系统服务,包括 SA(Service Ability,能力服务)。

1.3.1.代码分析

  BUILD.gn用于编译构建模块:

 base/startup/init/services/init/standard/BUILD.gn:15 init_common_sources = [16   "../init_capability.c",17   "../init_common_cmds.c",18   "../init_common_service.c",19   "../init_config.c",20   "../init_group_manager.c",21   "../init_service_file.c",22   "../init_service_manager.c",23   "../init_service_socket.c",24   "../main.c",25 ]33 ohos_executable("init") {34   sources = [35     "../adapter/init_adapter.c",36     "../standard/device.c",37     "../standard/fd_holder_service.c",38     "../standard/init.c",39     "../standard/init_cmdexecutor.c",40     "../standard/init_cmds.c",41     "../standard/init_control_fd_service.c",42     "../standard/init_jobs.c",43     "../standard/init_mount.c",44     "../standard/init_reboot.c",45     "../standard/init_service.c",46     "../standard/init_signal_handler.c",47     "../standard/switch_root.c",48   ]4950   modulemgr_sources = [51     "//base/startup/init/interfaces/innerkits/hookmgr/hookmgr.c",52     "//base/startup/init/interfaces/innerkits/modulemgr/modulemgr.c",53   ]54   sources += modulemgr_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);//第一次这里走的是SystemPrepareif (isSecondStage == 0) {SystemPrepare();} else {LogInit();}SystemInit();//启动rcs进程SystemExecuteRcs();SystemConfig();SystemRun();return 0;
}

  这里将init进程的代码分成了通用的和特有的两部分,共同的代码均在 /base/startup/init/services/init/文件夹下,其中有lite/和standard/分别用来构建小型系统和标准系统的init进程。这里主要分析标准进程的启动流程。

  • SystemPrepare 主要工作:
    挂载一些基本目录,比如/dev,/mnt,/storage,/dev/pts,/proc,/sys,/sys/fs/selinux,接着创建一些设备节点比如/dev/null,/dev/random,/dev/urandom 等,检查系统是否处于升级模式,如果不处于升级模式就启动init的第二阶段。

  由于从kernel进程启动的init进程并未传递任何参数,所以会先执行SystemPrepare:

base/startup/init/services/init/standard/init.c:
225 void SystemPrepare(void)
226 {
227     MountBasicFs();     //挂载一些基本目录并创建一些设备节点
228     CreateDeviceNode();
229     LogInit();
230     // Make sure init log always output to /dev/kmsg.
231     EnableDevKmsg();
232     INIT_LOGI("Start init first stage.");
233     HookMgrExecute(GetBootStageHookMgr(), INIT_FIRST_STAGE, NULL, NULL);
234     // Only ohos normal system support
235     // two stages of init.
236     // If we are in updater mode, only one stage of init.
237     if (InUpdaterMode() == 0) {  //检查是否处于升级模式,如果没有处于升级模式,就进入init第二阶段
238         StartInitSecondStage();
239     }
240 }
  • 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;// It will panic if close stdio before execv("/bin/sh", NULL)CloseStdio();//启动init进程的第二阶段INIT_LOGI("Start init second stage.");SwitchRoot("/usr");// Execute init second stagechar * 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。

1.3.2.init挂载required分区

  required分区就是系统启动引导过程的必要分区,必须在二级启动开始前进行挂载。比如system、vendor等必选镜像,挂载这些镜像前,需要先创建对应的块设备文件。这些块设备文件是通过内核上报UEVENT事件来创建的。init需要知道存储器的主设备目录,需要bootloader通过default_boot_device传递。

  目前init支持两种方式获取required分区信息,一是通过保存在/proc/cmdline中的bootargs,init会首先尝试从cmdline读取required分区信息;二是通过读取ramdisk中的/system/etc/fstab.required文件,只有在前一种方式获取失败的情况下才会尝试通过这种方式获取。

  • 块设备的创建逻辑

    • 准备工作

      • init从cmdline中读取required fstab,若获取失败,则尝试读fstab.required文件,从中获取必须挂载的块设备的PARTNAME,例如system和vendor.
      • 创建接收内核上报uevent事件广播消息的socket,从/proc/cmdline里读取default_boot_device。
      • 带着fstab信息和socket句柄遍历/sys/devices目录,准备开始触发内核上报uevent事件。
    • 触发事件

      • 通过ueventd触发内核上报uevent事件。
      • 匹配uevent事件中的partitionName与required fstab中的device信息。
      • 匹配成功后将会进一步处理,格式化设备节点路径,准备开始创建设备节点。
    • 创建节点

      • 为了便于用户态下对设备节点的访问以及提高设备节点的可读性,会对即将创建的required块设备节点同时创建软链接,这就需要先格式化软链接的路径。
      • 以上工作都完成后,将执行最后的创建设备节点的步骤,根据传入的uevent中的主次设备号、前置步骤中构建的设备节点路径和软链接路径等创建设备节点,并创建相应软链接。

至此,块设备节点创建完毕。

  • 与default_boot_device匹配关系

  内核将bootargs信息写入/proc/cmdline,其中就包含了default_boot_device,这个值是内核当中约定好的系统启动必要的主设备目录。以ohos.required_mount.为前缀的内容则是系统启动必要的分区挂载信息,其内容与fstab.required文件内容应当是一致的。另外,分区挂载信息中的块设备节点就是default_boot_device目录中by-name下软链接指向的设备节点。例如,default_boot_device的值为soc/10100000.himci.eMMC,那么ohos.required_mount.system的值就包含了/dev/block/platform/soc/10100000.himci.eMMC/by-name/system这个指向system设备节点的软链接路径。

  在创建块设备节点的过程中,会有一个将设备路径与default_boot_device的值匹配的操作,匹配成功后,会在/dev/block/by-name目录下创建指向真实块设备节点的软链接,以此在访问设备节点的过程中实现芯片平台无关化。

  arch/arm64/boot/dts/rockchip/rk3568-linux.dtsi:

bootargs = "earlycon=uart8250,mmio32,0xfe660000 console=ttyFIQ0 ohos.boot.eng_mode=on root=PARTUUID=614e0000-0000 hardware=rk3568 default_boot_device=fe310000.sdhci rw rootwait ohos.required_mount.system=/dev/block/platform/fe310000.sdhci/by-name/system@/usr@ext4@ro,barrier=1@wait,required ohos.required_mount.vendor=/dev/block/platform/fe310000.sdhci/by-name/vendor@/vendor@ext4@ro,barrier=1@wait,required ohos.required_mount.misc=/dev/block/platform/fe310000.sdhci/by-name/misc@none@none@none@wait,required ohos.required_mount.bootctrl=/dev/block/platform/fe310000.sdhci/by-name/bootctrl@none@none@none@wait,required";

1.2.2.服务启动

  init启动引导组件:

  • 每个系统服务启动时都需要编写各自的启动脚本文件init.cfg,定义各自的服务名、可执行文件路径、权限和其他信息。
  • 每个系统服务各自安装其启动脚本到/system/etc/init目录下,init进程统一扫码执行。
  • 当需要添加配置文件时,用户可以根据需要定义自己的配置文件,并拷贝到相应的目录下。

  init进程启动时,首先完成系统初始化工作,然后开始解析配置文件。系统在解析配置文件时,会将配置文件分成三类:

  • init.cfg(base/startup/init/services/etc/init.cfg)默认配置文件,由init系统定义,优先解析。
  • /system/etc/init/*.cfg各子系统定义的配置文件。
  • /vendor/etc/init/*.cfg厂商定义的配置文件。

1.2.2.1.init.cfg

  在init.cfg加载"/etc/init.usb.cfg", “/etc/init.usb.configfs.cfg”, /vendor/etc/init.${ohos.boot.hardware}.cfg"这几个config,会把它们的信息全部整合进来。

  2     "import" : [3             "/etc/init.usb.cfg",4             "/etc/init.usb.configfs.cfg",5             "/vendor/etc/init.${ohos.boot.hardware}.cfg"6     ],

  init.cfg主要负责:

  执行job,如果开发者的进程在启动之前需要首先执行一些操作(例如创建文件夹),可以把操作放到pre-init中先执行。一般pre-init阶段主要是为后面启动服务做准备的,比如挂载目录,设置权限,启动uevent、watchdog等,uevent主要是有些服务需要响应插拔事件才会被拉起。

  • pre-init
    最先执行的job,如果开发者的进程在启动之前需要首先执行一些操作(例如创建文件夹),可以把操作放到pre-init中先执行。

  • init
    中间执行的job,例如服务启动。

  • post-init
    最后被执行的job,如果开发者的进程在启动完成之后需要有一些处理(如驱动初始化后再挂载设备),可以把这类操作放到该job执行。单个job最多支持30条命令(当前仅支持start/mkdir/chmod/chown/mount/loadcfg),命令名称和后面的参数(参数长度≤128字节)之间有且只能有一个空格。

1.2.2.2.vendor/etc/init.rk3568.cfg

  主要是跟硬件产品相关的配置,这里rk3568, 可见主要是挂载了debugfs(调试子系统),修改设备角色为 peripheral,即设备。

1 {2     "import" : [3             "init.${ohos.boot.hardware}.usb.cfg"4     ],5     "jobs" : [{6             "name" : "pre-init",7             "cmds" : [8                 "write /proc/sys/vm/min_free_kbytes 10240",9                 "mount debugfs /sys/kernel/debug /sys/kernel/debug mode=755",10                 "write /sys/kernel/debug/hisi_inno_phy/role peripheral"11             ]12         }, {13             "name" : "init",14             "cmds" : [15                 "write /proc/1/oom_score_adj -1000",16                 "write /proc/sys/kernel/hung_task_timeout_secs 90",17                 "write /sys/kernel/hungtask/enable on",18                 "write /sys/kernel/hungtask/monitorlist whitelist,init,appspawn",19                 "chown system system /sys/kernel/hungtask/userlist",20                 "symlink /dev/block/platform/fe310000.sdhci/by-name /dev/block/by-name"21             ]22         }, {

refer to

  • https://huaweicloud.csdn.net/64df3b15dc60580edc7735f4.html
  • https://forums.openharmony.cn/forum.php?mod=viewthread&tid=3460
  • https://blog.csdn.net/isoftstone_HOS/article/details/126864246
  • https://blog.csdn.net/isoftstone_HOS/article/details/127420747
  • https://gitee.com/openharmony/docs/blob/master/zh-cn/device-dev/subsystems/subsys-boot-init-cfg.md

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/473014.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

力扣刷题日记之150.逆波兰表达式求值

今天继续给大家分享一道力扣的做题心得今天这道题目是 150.逆波兰表达式求值 题目如下&#xff0c;题目链接&#xff1a;https://leetcode.cn/problems/evaluate-reverse-polish-notation 1&#xff0c;题目分析 这道题说是一道中等难度的题目&#xff0c;其实如果理解了其中的…

Redis五大基本类型——String字符串命令详解(命令用法详解+思维导图详解)

目录 一、String字符串类型介绍 二、常见命令 1、SET 2、GET 3、MGET 4、MSET 使用MGET 和 使用多次GET的区别 5、DEL 6、SETNX SET、SET NX和SET XX执行流程 7、INCR 8、INCRBY 9、DECR 10、DECYBY 11、INCRBYFLOAT 12、APPEND 13、GETRANGE 14、SETRANGE …

如何知道表之间的关系(为了知识图谱的构建)

今天就简单点&#xff0c;把今天花时间做的一个程序说下。 我们在做常规知识图谱的时候&#xff0c;面临一个问题就是要知道关系是如何建立。如果表的数量比较少&#xff0c;人工来做还是比较容易的。 如果有非常多的表&#xff0c;并且这些表之间的关联关系都不清楚的情况下…

【软件测试】一个简单的自动化Java程序编写

文章目录 自动化自动化概念回归测试常见面试题 自动化测试金字塔 Web 自动化测试驱动 Selenium一个简单的自动化示例安装 selenium 库使⽤selenium编写代码 自动化 自动化概念 自动的代替人的行为完成操作。自动化在生活中处处可见 生活中的自动化可以减少人力的消耗&#x…

网络学习第四篇

引言&#xff1a; 我们在第三篇的时候出现了错误&#xff0c;我们要就行排错&#xff0c;那么我们要知道一下怎么配置静态路由实现ping通&#xff0c;这样子我们才知道下一跳到底是什么&#xff0c;为什么这样子做。 实验目的 理解和掌握静态路由的基本概念和配置方法。 实…

LeetCode题解:17.电话号码的数字组合【Python题解超详细,回溯法、多叉树】,知识拓展:深度优先搜索与广度优先搜索

题目描述 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits "23" 输出…

Python爬虫项目 | 一、网易云音乐热歌榜歌曲

文章目录 1.文章概要1.1 实现方法1.2 实现代码1.3 最终效果 2.具体讲解2.1 使用的Python库2.2 代码说明2.2.1 创建目录保存文件2.2.2 爬取网易云音乐热歌榜单歌曲 2.3 过程展示 3 总结 1.文章概要 学习Python爬虫知识&#xff0c;实现简单的一个小案例&#xff0c;网易云音乐热…

消息中间件分类

消息中间件&#xff08;Message Middleware&#xff09;是一种在分布式系统中实现跨平台、跨应用通信的软件架构。它基于消息传递机制&#xff0c;允许不同系统、不同编程语言的应用之间进行异步通信。 常见的消息中间件类型包括&#xff1a; 1. JMS&#xff08;Java Message S…

GoogleCloud服务器的SSH连接配置

首先&#xff0c;Google的服务器默认是通过自带的SSH网页端连接的&#xff0c;比较麻烦和容易断开&#xff0c;不是很好用&#xff0c;常见的解决办法有两种一种是通过修改ssh的配置&#xff0c;添加密码的方式进行连接&#xff0c;一种是通过配置公钥进行连接。 密码连接之前有…

31.3 XOR压缩和相关的prometheus源码解读

本节重点介绍 : xor 压缩value原理xor压缩过程讲解xor压缩prometheus源码解读xor 压缩效果 xor 压缩value原理 原理:时序数据库相邻点变化不大&#xff0c;采用异或压缩float64的前缀和后缀0个数 xor压缩过程讲解 第一个值使用原始点存储计算和前面的值的xor 如果XOR值为0&…

【图像压缩感知】论文阅读:Content-Aware Scalable Deep Compressed Sensing

tips&#xff1a; 本文为个人阅读论文的笔记&#xff0c;仅作为学习记录所用。本文参考另一篇论文阅读笔记 Title&#xff1a; Content-Aware Scalable Deep Compressed Sensing Journal&#xff1a; TIP 2022 代码链接&#xff1a; https://github.com/Guaishou74851/CASNet…

Neo4j Desktop 和 Neo4j Community Edition 区别

Neo4j Desktop 和 Neo4j Community Edition 的主要区别在于它们的用途、功能以及安装和管理方式。以下是这两者的详细对比&#xff1a; 1. Neo4j Desktop Neo4j Desktop 是一个图形化的桌面应用程序&#xff0c;主要为开发人员和个人使用提供了一个便捷的环境来安装、管理和运…

DAY120java审计第三方组件依赖库挖掘FastjsonShiroLog4jH2DB

组件漏洞判断插件 一、Tmall_demo-master&#xff08;fastjson&#xff09; 1、配置文件查找安装组件 1、JSON.parse(json) 2、JSON.parseObject 2、找可控的变量 3、利用组件漏洞 poc:propertyJson{"type":"java.net.Inet4Address","val":&q…

要查看你的系统是 x64(64位)还是 x86(32位),可以按照以下步骤操作

文章目录 1. 通过“系统信息”查看系统架构2. 通过“设置”查看系统架构3. 通过命令提示符查看系统架构4. 通过 PowerShell 查看系统架构5. 通过文件资源管理器查看系统架构总结 要查看你的系统是 x64&#xff08;64位&#xff09;还是 x86&#xff08;32位&#xff09;&…

通过JS删除当前域名中的全部COOKIE教程

有时候需要通过JS来控制一下网站的登录状态&#xff0c;就例如:网站登出功能&#xff0c;我们可以直接通过JS将所有COOKIE删除&#xff0c;COOKIE删除之后&#xff0c;网站自然也就退出了。 那么今天我就给大家分享一段JS的函数&#xff0c;通过调用这段函数就可以实现删除COO…

在Ubuntu22.04上源码构建ROS noetic环境

Ubuntu22.04上源码构建ROS noetic 起因准备环境创建工作目录并下载源码安装编译依赖包安装ros_comm和rosconsole包的两个补丁并修改pluginlib包的CMakeLists的编译器版本编译安装ROS noetic和ros_test验证 起因 最近在研究VINS-Mono从ROS移植到ROS2&#xff0c;发现在编写feat…

C++ 中的string类

本文主要通过文档形式使用C中string类的常见接口进行介绍&#xff0c;然后我们自己实现一个string类 标准库中的string 使用库中的string类时&#xff0c;必须包含头文件&#xff1a;#include<string>, 以及 using namespace std string 构造函数 首先我们来看构造函数…

html + css 自适应首页布局案例

文章目录 前言一、组成二、代码1. css 样式2. body 内容3.全部整体 三、效果 前言 一个自适应的html布局 一、组成 整体居中&#xff0c;宽度1200px&#xff0c;小屏幕宽度100% 二、代码 1. css 样式 代码如下&#xff08;示例&#xff09;&#xff1a; <style>* {…

Python知识点精汇!字符串:定义、截取(索引)和其内置函数

目录 一、字符串的定义 二、字符串的截取 1.截取干啥的 2.怎么用截取 3.打印多次 4.两个字符串拼接在一起 三、字符串内置函数 1.查询函数&#xff1a; &#xff08;1&#xff09;find(str,start,end) &#xff08;2&#xff09;index&#xff08;str,start,end&#…

mindspore发布件

MindSpore Repohttps://repo.mindspore.cn/ MindSpore Repohttps://repo.mindspore.cn/mindspore-lab/mindnlp/newest/any/