Loading...
首页专栏详情
打赏

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

 
1人已赏
易百纳技术社区 codinglab 2020-12-23 17:59:07
Hi,大家周末好呀。今天我们来看看Harmonyos是怎么样启动。

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

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

image-20201219094153686

其实就可以说明应用程序的入口是就是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的原因),有两处。

第一处:

image-20201219095609912

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

第二处:

image-20201219095550565

这是整个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,通过源码我们并没有找到这个的实现,那我们继续看汇编好了。

image-20201219102301372

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

应用的初始化

image-20201219103149162

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

image-20201219104808915

#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();
}

image-20201219110630453

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

总结:

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

3686
收藏
点赞
评论
0个
内容存在敏感词
打赏作者
codinglab
您的支持将鼓励我继续创作!
金额:
¥1 ¥5 ¥10 ¥50 ¥100
支付方式:
微信支付
支付宝支付
微信支付
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

易百纳技术社区
确定要删除此文章、专栏、评论吗?
确定
取消
易百纳技术社区