19

鸿蒙 OS 源码精读之 init 实战

 3 years ago
source link: https://blog.csdn.net/Innost/article/details/108789529
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

鸿蒙OS开源代码精要解读之——init

作者介绍:

中科创达OpenHarmony研究组

说明:

中科创达OpenHarmony研究组第一时间对 https://codechina.csdn.net/openharmony 上开源的代码进行了详尽的代码研读和学习。为此,我们打算编写一系列篇幅中等,内容精炼的源码分析文章来引领大家更进一步的走进鸿蒙OS。随着对代码的了解,广大开发者想亲自动手参与的意愿和信心也会随之增强——这也是鸿蒙OS开源的意义所在。

本篇内容摘要:

本篇以OpenHarmony中ipcamera_hi3518ev300为编译目标,介绍init进程的相关代码。

写在前面的话

我们对OpenHarmony的代码进行了一个简单粗略的统计。除去所有的third_party代码(即OpenHarmony使用的第三方开源库),其他剩余的代码中,以.c、.h文件为统计入口,总有效代码行数(不含注释、空行等,统计工具为tSourceCounter)为325627行。其中,归属kernel目录下的总有效代码行数为74150行。整个OpenHarmony中,kernel部分占比为22.8%左右,代码量上占大头的还在于kernel之上的、我们称之为Framework的部分。根据我们在Android系统上多年的摸索和经验,Framework恰恰是Android OS的精髓。所以,以OpenHarmony目前才20多万行的Framework代码量来看,感兴趣的开发者在这块参与共建、献策献力的机会非常大。

1.  OpenHarmony 源码的下载和编译

先介绍代码的下载和编译。我们研究组用得是ubuntu 19.10的主机环境。

1.1  源码下载

按照codechina.csdn官网的源码下载指南: https://codechina.csdn.net/openharmony/docs/-/blob/master/get-code/%E6%BA%90%E7%A0%81%E8%8E%B7%E5%8F%96.md

我们使用的是第四种方式“获取方式4:从代码仓库获取”。执行这一节中的几个命令,即可得到整个源码仓库。

1.2  编译源码

我们选择的编译目标是“Hi3518解决方案”,其编译后的输出目录名为ipcamera_hi3518ev300。ipcamera_hi3518ev300是一个基于海思的ip摄像头设备。相关的介绍文档入口在 https://codechina.csdn.net/openharmony/docs/-/blob/master/quick-start/%E6%90%AD%E5%BB%BA%E7%8E%AF%E5%A2%83-2.md

注意,编译不同的解决方案需要建立对应的编译环境。对hi3518来说,开发者需要按照上述链接里的“搭建环境”来下载和配置。

一切就绪后,在源码根目录下执行 python build.py。如果不带参数的话,它会提醒你指定编译目标,截图如下:

imueAbQ.png!mobile

图1  python build.py不带参数的执行结果

这次,我们通过python build.py ipcamera_hi3518ev300即可编译“Hi3518解决方案”。编译耗时10几分钟。

注意,编译过程中可能出现找不到<valgrind/valgrind.h>的错误。这是因为目前我们下载的代码中没有包含valgrind的头文件。开发者可以手动将/usr/include/valgrind目录拷贝到prebuilts/lite/sysroot/usr/include下即可(仅限Ubuntu平台,需提前安装好valgrind工具)。

1.3  OpenHarmony编译相关小知识介绍

OpenHarmony源码编译系统使用了google开发的gn工具以及ninjia。这二者结合起来比传统的makefile编译系要高效,尤其适合大系统的并行编译。对开发者而言,如果要参与OpenHarmony的开发,需要对gn的语法有些了解。本文仅做一些最基本的介绍:

  1. 使用gn工具的话,开发者将编译规则写在名为BUILD.gn文件中。和Makefile一样,gn文件有自己的语法规则,属于领域语言(Domain Specific Language,DSL)。gn语法不难,但编译规则本身有很多内容,所以一下子要掌握全部内容也不容易。
  2. gn支持自定义模板函数,可放在名为.gni的文件中。OpenHarmony中最常见到的gn模板文件为./build/lite/config/component/lite_component.gni。.gn文件中通过import可导入gni模板文件。OpenHarmony定义了lite_component、lite_library等模板函数。
  3. gn中,可执行文件的编译函数入口为exectuable("文件名"),共享库的编译规则函数为shared_library("文件名")。所以,如果要搜索某个文件对应的编译规则,可以先搜索所有的BUILD.gn文件,然后grep executable。以下是我们grep所有的executable的结果截图。

2QVriem.png!mobile

图2  grep BUILD.gn中executable的结果示意

通过这种方式,我们能很快定位到比如init对应的代码在什么地方。

最后,我们再简单介绍下OpenHarmony编译系统中和底层OS有关的一个条件编译控制变量ohos_kernel_type。目前,该变量有四个取值,分别为"liteos_a"、"liteos_m"、"liteos_riscv"和"linux":

  1. "liteos_a"和"linux"经常做为一组进行判断。liteos_a实际对应的是Cortex-A系列,其性能相对较高,可以跑Linux系统。
  2. "liteos_m"和"liteos_riscv"往往是一组的。liteos_m对应的是Cortex-M系列,liteos_riscv是Riscv芯片的表示,二者可能都是针对性能一般,功耗较低的设备。

ohos_kernel_type的取值由build/lite/product/解决方案名.json文件中的product字段决定。例如,我们选择的ipcamera_hi3518ev300的配置文件内容截图如下,它的kernel字段值为"liteos_a"。

iMza6vY.png!mobile

图3  build/lite/product/ ipcamera_hi3518ev300.json配置文件示意图

编译完成后,所有编译生成物都在out/ipcamera_hi3518ev300目录下。

2 init 源码精要解析

init是Linux系统上的第一个应用进程,是其它进程的源头。对ipcamera_hi3518ev300来说,它的编译产物中也有一个init进程。

在上面提到的out/ipcamera_hi3518ev300目录下,有一个rootfs.tar文件。这个文件里就是设备上根文件系统的内容。打开其中的/rootfs/bin目录,可以看到此次编译的可执行程序如下截图所示:

yyMVZvf.png!mobile

图4  out/ipcamera_hi3518ev300/rootfs.tar/bin内容示意

借助图2里提到的办法,我们可以定位到init对应的代码路径为base/startup/services/init_lite/。其内容如下图所示:

jQjYFb.png!mobile

图5  init_lite源码文件示意图

main.c是整个init的入口。我们简单看一下它的代码,如下所示。

6fuyAj3.png!mobile

图6  init_lite/main.c

init main函数非常精简,非常符合"lite"轻量简便的风格。当然,也不排除未来init的代码会越来越复杂。我们在AOSP上观察到的情况就是一个例子——AOSP里现在的init的相关代码非常复杂)。

我们对InitReadCfg比较感兴趣,这个函数内部将读取/etc/init.cfg文件。这个文件在图4中提到的rootfs.tar中可以找到,下图是其内容的示意:

mEfIFbq.png!mobile

图7  rootfs.tar/etc/init.cfg

init.cfg本质上是一个json格式的文件。它包括一个名为"jobs"的数组和一个名为"services"的数组。

  1. 对"jobs"来说:内部分别包含"pre-init"、"init"和"post-init"三个元素。从上面的截图中可以看出,这三个元素对应的就是设置挂载一些设备、设置好路径,启动服务等工作。
  2. 对"services"来说:它包含一组服务的定义。所谓的服务,就是系统里的关键进程。可以猜测到,init将根据service的配置来启动对应的服务程序,并设置它的uid、gid、进程优先级和权限等。

如果开发者对Android系统有一定了解的话,会发现OpenHarmony和AOSP在init的工作流程上有着相似的设计思路。不过,对OpenHarmony目标设备来说,使用json格式无疑是比较简单且方便的。

最后,我们再看一下init的另外一个重要职能——服务进程状况监控。init.cfg中的那些服务属于系统关键进程。运行过程中如果它们出现异常导致进程退出,需要有个办法将它们重新启动以保证业务连续性。

这个功能的实现就是利用Linux系统的SIGCHILD信号。init在SignalInitModule中监听了该信号并设置了对应的信号处理函数——SigHandler。SigHandler函数的具体处理过程则比此处说得要更复杂一点。现在,这部分内容就留给读者们自行探索了!!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK