鸿蒙系统中HelloWorld程序是如何被调用的?
相信大家都已经在鸿蒙系统上实现了自己的第一个 HelloWorld 程序了。

代码很简单,编译烧录后,我们就可以看到串口有打印 [DEMO] Hello world.。
但是 HelloWorld 函数是在何时被调用的呢?SYS_RUN 又是干嘛的呢?我们来看下。
01
启动流程
首先,我们需要分析一下 Hi3861 的启动流程。目前 Hi3861 使用的是 liteOS-M 内核,相关源码厂家没有提供,不过也不妨碍我们。
(vendor\hisi\hi3861\hi3861\app\wifiiot_app\src\app_main.c)
hi_void?app_main(hi_void)
{
?//打印sdk版本
?const?hi_char*?sdk_ver?=?hi_get_sdk_version();
????printf("sdk?ver:%s\r\n",?sdk_ver);
?//串口、IO初始化等
?peripheral_init();
?//wifi初始化
?ret?=?hi_wifi_init(APP_INIT_VAP_NUM,?APP_INIT_USR_NUM);
?//鸿蒙系统初始化
?HOS_SystemInit();
}
我们可以看到其实 app_main 启动后做了很多工作,包括 io 初始化、wifi 初始,最后调用了 HOS_SystemInit();进行鸿蒙系统最后的初始化。
base\startup\services\bootstrap_lite\source\system_init.c
void?HOS_SystemInit(void)
{
????MODULE_INIT(bsp);
????MODULE_INIT(device);
????MODULE_INIT(core);
????SYS_INIT(service);
????SYS_INIT(feature);
????MODULE_INIT(run);
????SAMGR_Bootstrap();
}
看起来好像在调用某些模块,仔细看,其中有一个是 MODULE_INIT(run);。
顾名思义,好像在初始化或者调用 一个 run
模块。那 run 模块又是什么呢?
我们看下标题的 SYS_RUN(HelloWorld)。是不是可以猜测其实 MODULE_INIT(run);就是调用了 HelloWorld 呢?
../../base/startup/services/bootstrap_lite/source/system_init.c?38?
../../applications/sample/wifi-iot/app/my_first_app/hello_world.c?9?
[DEMO]?Hello?world.
../../base/startup/services/bootstrap_lite/source/system_init.c?40
仔细看我加的打印语句,确实是在 38 行执行 MODULE_INIT(run);后才打印 [DEMO] Hello world.。

02
链接
#define?MODULE_INIT(name)?????\
????do?{??????????????????????\
????????MODULE_CALL(name,?0);?\
????}?while?(0)

(MODULE_BEGIN(name,?step))
#define?MODULE_NAME(name,?step)?".zinitcall."?#name?#step?".init"
我这里再帮大家展开,其实".zinitcall." #name #step ".init" 最后就是 .zinitcall.run2.init。
它其实是一种写法,就是说我们代码编译的时候,代码里面有一段地址比较特殊,它的名字是 .zinitcall.run2.init ,也就是说 InitCall 指针指向的是 .zinitcall.run2.init 代码段的地址。
画个图:

绿色的是 .zinitcall.run2.init 代码段,里面存放着函数指针。


到了这里就剩下最后一个问题了:怎么让它指向 HelloWorld 函数。
这里其实就是 SYS_RUN的功劳了。我们也来看SYS_RUN做了什么,其它也是一个宏,展开过程如下:
static?const?InitCall?USED_ATTR?__zinitcall_##layer##_##func?\
????????__attribute__((section(".zinitcall."?clayer?#priority?".init")))?=?HelloWorld

static?const?InitCall??=?HelloWorld
是不是很简单,其实就是定义了一个全局变量(函数指针),它指向 HelloWorld 。
那红色字体是做什么用呢?它其实就是告诉编译,我这个变量(static const InitCall 变量),很特殊,编译的时候给我编译在 .zinitcall.run2.init 段。

03
忠告
这里有两个忠告:
①请不要直接在 SYS_RUN() 定义的入口函数直接写 while(1)
这个很简单理解了,因为系统启动后,app_main 会调用到 我们定义的 SYS_RUN() 定义的入口函数,比如 HelloWorld。
如果我们在 HelloWorld 函数中写了 while(1) 就会导致 app_main 后续的代码得不到执行,肯定有问题。
②SYS_RUN() 定义的入口函数创建的线程,请一定要有 sleep 动作
为了解决第一个问题,我们很自然地想到,可?栽?SYS_RUN() 定义的入口函数
创建线程,这样就可以 while(1) 了。
哈哈,其实也是有问题,因为 app_main
本身也是一个任务,如果我们自己创建地任务优先级特别高,就会导致 app_main 任务不会被执行,还是有问题。
所以要有 sleep,确保 app_main 后续地代码能顺利执行下去。
关注公众号:拾黑(shiheibook)了解更多 [广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
关注网络尖刀微信公众号随时掌握互联网精彩
- 1 习近平将发表二〇二六年新年贺词 7904141
- 2 2026年国补政策来了 7808738
- 3 东部战区:开火!开火!全部命中! 7712893
- 4 2026年这些民生政策将惠及百姓 7616985
- 5 小学食堂米线过期2.5小时被罚5万 7519709
- 6 解放军喊话驱离台军 原声曝光 7428214
- 7 为博流量直播踩烈士陵墓?绝不姑息 7327605
- 8 每月最高800元!多地发放养老消费券 7238391
- 9 数字人民币升级 1月1日起将计付利息 7141831
- 10 2026年1月1日起 一批新规将施行 7040675








51CTO技术栈
