首页 > 技术知识 > 正文

Hi,大家周末好呀。今天我们来看看Harmonyos是怎么样启动。

程序入口:main和app_main的那些事

玩过liteos的同学都比较熟悉了,liteos的内核启动后的第一个程序入口是app_main(),小A根据经验推测目前HarmonyOS的应用程序入口函数也是app_main(), 如何证明呢?其实如果小伙伴们留意的话,在之前vscode的调试配置中有这么一项

[HarmonyOS之旅]  Chapter4 – HarmonyOS启动流程

其实就可以说明应用程序的入口是就是app_main。那么这里有个问题了,为什么不是main为入口呢?Hi3861采用的是liteos-m核,而liteos的一个特点就是内核和应用的关系和linux的不一。Linux支持多进程,所以可以有多个main运行,而liteos并不支持多进程,那么其实内核和应用实际上就是一个整体的程序,而不是独立分开的,因此不能有多个main。那么问题来了,这么说的话,意思就是有了一个main入口,因此后面才使用app_main作为内核启动后的第一个程序入口。下面请看小A的证明😉。

在编译完成后,会生成这么一个文件out/wifiiot/Hi3861_wifiiot_app.asm,这就是app的汇编文件。我们打开看一看,很大很长。我们秉持着怀疑的态度,搜索一下 “< main >”(main前后其实无空格,这里是因为markdown的原因),有两处。

第一处:

[HarmonyOS之旅]  Chapter4 – HarmonyOS启动流程1

根据这一段汇编,我们可以知道,一个叫做start_falsh_data_loop的函数在最后跳转到main函数了。另外根据这三段汇编,我们可以大致猜测,他们做的事情就是,把代码从flash拷贝到ram中,然后执行程序。因此这里的main就是这个系统的入口函数了。

第二处:

[HarmonyOS之旅]  Chapter4 – HarmonyOS启动流程2

这是整个main函数的汇编体了,从中我们可以主要有调用以下函数:

hi_u32 change_uart(hi_uart uart_num); hi_void hi_patch_init(hi_void); uart_puts; LITE_OS_SEC_TEXT void register_log_hook(OS_SENDCHR2CHL_HOOK_FUNC uartFun); CheckChipVer; OsBoardConfig; LITE_OS_SEC_TEXT_INIT UINT32 LOS_KernelInit(VOID) AppInit; LITE_OS_SEC_TEXT_INIT UINT32 LOS_Start(VOID)

这一段的重点是 LOS_KernelInit、AppInit、LOS_Start这三个,根据这三个调用顺序,我们可以看到内核初始化后的第一件事情是AppInit,通过源码我们并没有找到这个的实现,那我们继续看汇编好了。

[HarmonyOS之旅]  Chapter4 – HarmonyOS启动流程3

小伙伴们看到了没有,这里就出现了我们想要的app_main啦😏,应用程序的入口就由此可以确定了。

应用的初始化

[HarmonyOS之旅]  Chapter4 – HarmonyOS启动流程4

这是app_main的函数主体,这里我们可以看到这个和我们上一篇的大致分析是一致的,只能匹配到很少的一部分,那么我们来看下HOS_SystemInit();这个接口。

[HarmonyOS之旅]  Chapter4 – HarmonyOS启动流程5

#define SYS_INIT(name) \ do { \ SYS_CALL(name, 0); \ SYS_CALL(name, 1); \ SYS_CALL(name, 2); \ SYS_CALL(name, 3); \ SYS_CALL(name, 4); \ } while (0) #define MODULE_INIT(name) \ do { \ MODULE_CALL(name, 0); \ MODULE_CALL(name, 1); \ MODULE_CALL(name, 2); \ MODULE_CALL(name, 3); \ MODULE_CALL(name, 4); \ } while (0) #define SYS_CALL(name, step) \ do { \ InitCall *initcall = (InitCall *)(SYS_BEGIN(name, step)); \ InitCall *initend = (InitCall *)(SYS_END(name, step)); \ for (; initcall < initend; initcall++) { \ (*initcall)(); \ } \ } while (0) #define MODULE_CALL(name, step) \ do { \ InitCall *initcall = (InitCall *)(MODULE_BEGIN(name, step)); \ InitCall *initend = (InitCall *)(MODULE_END(name, step)); \ for (; initcall < initend; initcall++) { \ printf(“init Func Address:%p\n”, initcall); \ printf(“Call Func Address:%p\n”, *initcall); \ (*initcall)(); \ } \ } while (0)
<

很奇怪,SAMGR_Bootstrap()函数的调用之前,我们并没有明显的看到诸如log这样的模块的调用,那么类似模块的函数调用是怎么完成的呢?也许你会说,调用可能藏在别的地方,只是你没有分析到而已。Ok,那我们根据官方文档先来写个简化的Hello World先。

void HelloWorld(void) { printf(“Codinglabs >>>>>> Hello HarnonyOS\n”); } SYS_RUN(HelloWorld); void HOS_SystemInit(void) { printf(“%s %s %d \n”, __FILE__, __FUNCTION__, __LINE__); printf(“Codinglabs >>>>>> start\n”); MODULE_INIT(bsp); MODULE_INIT(device); MODULE_INIT(core); SYS_INIT(service); SYS_INIT(feature); MODULE_INIT(run); printf(“Codinglabs >>>>>> end\n”); SAMGR_Bootstrap(); }

[HarmonyOS之旅]  Chapter4 – HarmonyOS启动流程6

这是执行的结果,是不是很奇怪,我们明明没有在HOS_SystemInit中调用HelloWorld,怎么会在start和end之间被执行到了呢?

总结:

其实上面问题的答案就藏在马赛克里面,欲知后事如何,那么且听下回分解吧😛。

猜你喜欢