更多内容请访问:https://harmonyos.51cto.com,与华为官方共同打造的鸿蒙技术社区,分析其如何运行helloworld程序被调用了,SYS_RUN是干什么的?相信大家都在鸿蒙系统上实现了自己的第一个helloworld程序。.代码非常简单。编译烧录后可以看到串口打印了[DEMO]Helloworld。但是什么时候调用HelloWorld函数呢?SYS_RUN有什么作用?让我们来看看。1.启动过程首先,我们需要分析一下Hi3861的启动过程。目前Hi3861使用的是liteOS-M内核,相关源码厂商没有提供,但这并不妨碍我们。经过一番查找,得知hi3861启动后,第一个入口函数是app_main函数。(vendor\hisi\hi3861\hi3861\app\wifiiot_app\src\app_main.c)打开可以看到app_main函数的内容,如下。当然,我这里只是简化版。我删除了很多初始化函数,只保留了Ultimate。hi_voidapp_main(hi_void){//打印sdk版本consthi_char*sdk_ver=hi_get_sdk_version();printf("sdkver:%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();进行最后的鸿蒙系统初始化。那我们看看HOS_SystemInit();做。打开源码base\startup\services\bootstrap_lite\source\system_init.c可以看到函数内容如下:voidHOS_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);顾名思义,它似乎是在初始化或调用一个运行模块。什么是运行模块?我们来看题目的SYS_RUN(HelloWorld)。是否可以猜测MODULE_INIT(run);正在调用HelloWorld?哈哈哈~其实是真的。如果添加打印信息,可以看到如下打印。../../base/startup/services/bootstrap_lite/source/system_init.c38../../applications/sample/wifi-iot/app/my_first_app/hello_world.c9[DEMO]Helloworld.../../base/startup/services/bootstrap_lite/source/system_init.c40仔细看看我添加的打印语句。MODULE_INIT(run);确实在第38行;在打印之前打印[DEMO]Helloworld.所以和我们的猜测是一样的。当然是没完没了的,我们要分析为什么会这样。2.看看MODULE_INIT(run);做。实际上,它只是一个宏。#defineMODULE_INIT(name)\do{\MODULE_CALL(name,0);\}while(0)andMODULE_CALL(name,0);可以扩展:当然里面if语句的打印是我后面加上的。我们可以看到它实际上定义了一个InitCall指针,然后这个指针是这样的:(MODULE_BEGIN(name,step))而MODULE_BEGIN宏实际上展开如下:#defineMODULE_NAME(name,step)".zinitcall."#name#step".init"我这里帮你展开,其实就是".zinitcall"。#name#step".init"最后是.zinitcall.run2.init,其实是一种写法,也就是说,我们编译代码的时候,代码里面有一段地址,比较特殊,它的名字是.zinitcall.run2.init,也就是说InitCall指针指向.zinitcall.run2.init代码段的地址。画一张图:绿色的是.zinitcall.run2.init代码段,存放的是函数指针。好了,到这里大家应该明白了,继续看这张图,其实这里只是把这段代码段中的函数指针全部取出来,然后执行函数指针指向的函数。聪明的你应该猜到.zinitcall.run2.init代码段中的函数指针指向的是HelloWorld函数~~~这里最后一个问题:如何让它指向HelloWorld函数。这其实是SYS_RUN的功劳。让我们也看看SYS_RUN做了什么。另一个是宏。展开过程如下:我们可以看到SYS_RUN(HelloWorld)最后的结果是:staticconstInitCallUSED_ATTR__zinitcall_##layer##_##func\__attribute__((section(".zinitcall."clayer#priority".init")))=HelloWorld看起来很复杂,我们要拆解一下:先不看红色字体部分,那么结果就是:staticconstInitCall=HelloWorld不是很简单,其实就是定义一个全局变量(函数指针),它指向HelloWorld。红色字体是做什么用的?它实际上告诉编译器我的变量(staticconstInitCall变量)很特别。编译的时候会在.zinitcall.run2.init段编译。到这里,一切都清楚了,最后放一张启动流程图来总结一下:3.忠告这里有两个忠告:1.请不要在SYS_RUN()定义的入口函数中直接写while(1)——这个很简单我明白了,因为系统启动后,app_main会调用SYS_RUN()定义的入口函数,比如HelloWorld。如果我们在HelloWorld函数中写while(1),app_main后面的代码就不会执行,肯定有问题。2、SYS_RUN()定义的入口函数创建的线程必须有休眠动作。为了解决第一个问题,我们自然想到可以在SYS_RUN()定义的入口函数中创建线程,这样就可以使用while(1)了。哈哈,其实有问题,因为app_main本身也是一个task。如果我们创建的任务的优先级特别高,app_main任务不会执行,还是有问题。所以必须要有sleep才能保证app_main后面的代码能够顺利执行。更多信息请访问:Harmonyos.51cto.com,与华为官方合作打造的鸿蒙技术社区
