文章转载自:liangkz
声明:
升级文档到v2.0后可以声明是原创的了。这是上了朱有鹏老师的免费课《想读懂鸿蒙2.0源码,也许你需要先懂这些》之后,做的一些总结。
课程时间一个半小时,内容也很多,学习过程中我发现朱老师的ppt上部分代码/文件,在我本地的鸿蒙系统代码上找不到,或者路径不相同,所以我就做了一些整理。
这里仅摘取课程中的鸿蒙系统在HI3516DV300平台上的启动流程部分(从30:00开始讲解启动过程)进行汇总和整理,如有错误,请朱老师和各位同学指正。后继在学习过程中会继续对本文当作修正升级。
v1.0/2.0版本都是基于Hi3516平台+LiteOS_A内核的启动流程分析,即Part 1【鸿蒙系统的启动流程v2.0】
v3.0 版本开始增加基于Hi3861平台+LiteOS_M内核的启动流程分析,即Part 2。
更新记录:
Part 2: 基于Hi3861平台+LiteOS_M内核
说明:
1. 我本地的OpenHarmony 1.1.0 LTS(2021-04-01)代码根目录OHOS1_1_0LTS,如Part 1前文所述。
2. 我有Hi3861_Wifiiot开发板和开发环境,如下:
2.1 在Linux环境下的DecEco IDE下创建新工程“Test_Wifiiot”,在“HPM”标签下找到“@ohos/wifi_iot”, 点击“Install to project”选择“Test_Wifiiot”项目,开始下载并安装组件到项目里。如下图:
2.2 打开工程,可以一键build,也可以在终端输入“python build.py wifiiot”指令进行build。
2.3 在Window主机下,打开DevEco IDE,做好相关配置,连接好Hi3861开发板,烧录上面编好的软件,也可以用HiBurn工具进行烧录。
2.4 烧录完成后,重启开发板,抓取log分析。
3. 网上找到的信息,
Hi3861平台的LiteOS_M内核是固化在板子ROM上的,官方文档对Hi3861开发板的说明并没 有提到这一点,见【Hi3861开发板介绍】。
也就是说上面的烧录过程,其实并不会烧录内核部分的代码。
甚至在上面的Test_Wifiiot项目中都不会编译 LiteOS_M的内核代码,可以查看build脚本和build log:
Test_Wifiiotvendorhisihi3861hi3861uilduild_tmplogsuild_kernel.log
进行分析确认。
实际上我是这样确认的:
修改 Test_Wifiiotkernelliteos_mkernellos_init.c (或者子目录的los_xxx.c文件),添加log,故意写入错误的语法,希望触发编译错误。实际编译OK,说明其实没有编译到这部分代码。烧录软件后,添加的log,一个都没打印出来。
4. Test_Wifiiotkernelliteos_m目录下的kernel 虽然没有编译,但是components是有编译的,因为这下面是非常 重要的KAL组件。
KAL(Kernel Abstract Layer,内核抽象层),是鸿蒙系统框架层(Framework)与内核(LiteOS_M、LiteOS_A、Linux内核) 之间的接口,鸿蒙系统框架层与内核层是通过KAL接口进行隔离和解耦的。
KAL可以按照cmsis标准或者posix标准来实现Framework和kernel的对接,目前代码看到的是按cmsis-rtos v2 标准来实现的,详情见components 目录下的代码,可以在里面加log,跑起来可以打印log。
这个components就非常重要了,例如,在helloworld上创建线程时,需要在BUILD.gn中include "//kernel/liteos_m/components/cmsis/2.0" 路径,代码中#include "cmsis_os2.h"头文件,否则会编译失败。
5. 因为上面3的原因,我们无法抓取LiteOS_M内核的log进行分析,只能直接看OHOS1_1_0LTS中的LiteOS_M内核代码进行分析了。
1. 第一阶段:U-Boot启动
无log。
OHOS1_1_0LTSkernelliteos_m
targets
这个目录下是目前liteos-m官方已经支持的目标平台软硬件环境,我们目前拿到的官方Hi3861开发平台,应该 就是cortex-m7_nucleo_f767zi_gcc 这一个。
注意,Test_Wifiiotkernelliteos_m目录下,并
没有targets目录。
tagets目录下的STM32F767ZITx_FLASH.ld 就是bootloader引导程序,见该文件头部“Abstract”的描述。
- /* Entry Point */
- ENTRY(Reset_Handler)
复制代码
2. 第二阶段:汇编代码引导LiteOS-M内核
OHOS1_1_0LTSkernelliteos_m agets目录下的startup_stm32f767xx.s 就是汇编代码引导程序。
- * @Brief This is the code that gets called when the processor first
- * starts execution following a reset event. Only the absolutely
- * necessary set is performed, after which the application
- * supplied main() routine is called.
复制代码
代码入口在:
Reset_Handler:
跑到到下面:
- /* Call the clock system initialization function.*/
- bl SystemInit
- /* Call static constructors */
- bl __libc_init_array
- /* Call the application's entry point.*/
- bl main
- bx lr
复制代码
SystemInit代码在:
- OHOS1_1_0LTSkernelliteos_m argetscortex-m7_nucleo_f767zi_gccCoreSrcsystem_stm32f7xx.c
- * @brief Setup the microcontroller system
- * Initialize the Embedded Flash Interface, the PLL and update the
- * SystemFrequency variable.
复制代码
main则是进入内核LiteOS-M启动的C语言阶段的入口。
3. 第三阶段:内核LiteOS-M的C语言启动阶段
上面汇编阶段调用的main函数,位于:
OHOS1_1_0LTSkernelliteos_m argetscortex-m7_nucleo_f767zi_gccCoreSrcmain.c
Line95的调用的RunTaskSample(); 见同目录下的 task_sample.c
- VOID RunTaskSample(VOID)
- {
- UINT32 ret;
- ret = LOS_KernelInit();
- if (ret == LOS_OK) {
- TaskSample();
- LOS_Start();
- }
- }
复制代码
1.
LOS_KernelInit()
位于OHOS1_1_0LTSkernelliteos_mkernelsrclos_init.c
System kernel initialization function, configure all system modules
开始初始化liteos_m系统内核
2. TaskSample()
同在task_sample.c文件内定义,上面初始化系统内核成功后,创建两个sample task。
3. LOS_Start()
位于OHOS1_1_0LTSkernelliteos_mkernelsrclos_init.c
开始任务调度
4. 第四阶段:鸿蒙系统框架层的启动
以上三个阶段,都是在完整的鸿蒙系统代码
OHOS1_1_0LTS工程下查看和分析的,从这里开始就需要切换回到
Test_Wifiiot工程上去阅读、修改、编译代码,烧录bin,抓log分析了。
LiteOS-M的内核源代码我没怎么看,具体怎么切换到鸿蒙系统框架层去运行,我还不大清楚。
从Hi3861开发板上抓取的log,第一行就是“
ready to OS start”,表明LiteOS-M内核已经启动OK了。
接下来就进入到鸿蒙系统的框架层的启动,这一部分与 Part 1 的第4节Hi3516平台上的鸿蒙系统的框架层的启动有一些差别。
这个入口,并不是"
/bin/init",而是
app_main.c文件的
app_main()函数。
Test_Wifiiotvendorhisihi3861hi3861appwifiiot_appsrcapp_main.c
- hi_void app_main(hi_void)
- {
- printf("#########################################################
- ");
- printf("[app_main] LiteOS-M Kernel(ROM) inited.
- ");
- printf("[app_main] app_main:: Begin:
- ");
- .
- . //中间做了一大堆硬件初始化,调用的接口都被封装了的。
- .
- printf("[app_main] ::HOS_SystemInit():
- ");
- HOS_SystemInit();
- printf("[app_main] app_main End!
- ");
- printf("#########################################################
- ");
- }
复制代码
接下来我将贴上一段伪代码,加上我写的log,通过在Hi3861开发平台上抓取log来做对应的分析以便理解其启动流程。
log的格式一般来说是“[c文件名] 函数名 打印信息”,如:
“[cmsis_liteos2] osThreadNew: TaskName[HmosTask] TaskID[9]”
表示这句log在 cmsis_liteos2.c 文件内的osThreadNew()函数里打印“TaskName[HmosTask] TaskID[9]”信息。
code@ 标记函数所在代码文件路径
伪代码段开始:
- code@foundationdistributedscheduleservicessamgr_lite
- printf("[system_init] SAMGR_Bootstrap()=============================
- ");
- SAMGR_Bootstrap();
- {
- printf("[samgr_lite] SAMGR_Bootstrap. Begin: size=%d
- ",size);
- printf(" InitializeAllServices: size=%d
- ",size);
- Init service:Bootstrap TaskPool:0xfa408
- Init service:Broadcast TaskPool:0xfaa78
- Init service:hiview TaskPool:0xfac38
- static void InitializeAllServices(Vector *services)
- printf("[samgr_lite] SAMGR_Bootstrap. End.
- ");
- }
- printf("[system_init] HOS_SystemInit end. %%%%%%%%%%%%%%%%%%%%%%%%%%
- ");
- }
- printf("[app_main] app_main End!
- ");
- printf("#########################################################
- ");
- }
复制代码 伪代码段结束。
编译、烧录、上电、抓log,log基本上就能说明启动流程了,这里就不再进一步分析了,各位跑一遍代码就清楚了。
log段开始:
log段结束。
5. 鸿蒙应用(APP)的启动
我在Test_Wifiiotapplicationssamplewifi-iotapp目录下放置了两个应用,BUILD.gn文件如下:
- lite_component("app") {
- features = [
- "startup",
- "helloworld:helloworld",
- "iothardware:led",
- ]
- }
复制代码两个应用的入口函数分别是:
- SYS_RUN(helloworld);
- SYS_RUN(LedEntry);
复制代码这两个应用都在上面伪代码段中的MODULE_INIT(run);的时候被调用启动了。