|
|
|
|
公众号矩阵

分析鸿蒙系统helloworld程序是如何被调用,SYS_RUN做什么事情

文章由鸿蒙社区产出,想要了解更多内容请前往:51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com/?zz

作者:佚名来源:鸿蒙社区|2020-11-02 10:23

想了解更多内容,请访问:

51CTO和华为官方战略合作共建的鸿蒙技术社区 

https://harmonyos.51cto.com/#zz

分析 helloworld程序是如何被调用,SYS_RUN做什么事情

相信大家都已经在鸿蒙系统上实现了自己的第一个helloworld程序了。

 

代码很简单,编译烧录后,我们就可以看到串口有打印 [DEMO] Hello world.

但是 HelloWorld 函数是在何时被调用的呢?SYS_RUN 又是干嘛的呢?

我们来看下。

1. 启动流程

首先,我们需要分析一下Hi3861的启动流程。目前Hi3861 使用的是liteOS-M内核,相关源码厂家没有提供,不过也不妨碍我们。经过我一番查找,可以知道hi3861启动内后,第一个入口函数是 app_main函数。

(vendor\hisi\hi3861\hi3861\app\wifiiot_app\src\app_main.c)

大家可以打开,看到app_main函数的内容,如下,当然我这里只是简版的,我删除了很多初始化的函数,只保留最终要的。

  1. hi_void app_main(hi_void) 
  2.  //打印sdk版本 
  3.  const hi_char* sdk_ver = hi_get_sdk_version(); 
  4.     printf("sdk ver:%s\r\n", sdk_ver); 
  5.  
  6.  //串口、IO初始化等 
  7.  peripheral_init(); 
  8.  
  9.  //wifi初始化 
  10.  ret = hi_wifi_init(APP_INIT_VAP_NUM, APP_INIT_USR_NUM); 
  11.  
  12.  
  13.  //鸿蒙系统初始化 
  14.  HOS_SystemInit(); 

我们可以看到其实app_main启动后做了很多工作,包括io初始化、wifi初始,最后调用了HOS_SystemInit(); 进行鸿蒙系统最后的初始化。

那我们看下 HOS_SystemInit(); 做了什么动作吧。

打开源码 base\startup\services\bootstrap_lite\source\system_init.c

可以看到函数内容如下:

  1. void HOS_SystemInit(void) 
  2.     MODULE_INIT(bsp); 
  3.     MODULE_INIT(device); 
  4.     MODULE_INIT(core); 
  5.     SYS_INIT(service); 
  6.     SYS_INIT(feature); 
  7.     MODULE_INIT(run); 
  8.     SAMGR_Bootstrap(); 

看起来好像在调用某些模块,仔细看,其中有一个是 MODULE_INIT(run); 。顾名思义,好像在初始化或者调用 一个 run 模块。那run模块又是什么呢?我们看下标题的 SYS_RUN(HelloWorld)。 是不是可以猜测其实MODULE_INIT(run); 就是调用了 HelloWorld 呢?

哈哈哈~其实还真是。大家如果加打印信息,可以看到如下打印。

  1. ../../base/startup/services/bootstrap_lite/source/system_init.c 38  
  2.  
  3. ../../applications/sample/wifi-iot/app/my_first_app/hello_world.c 9  
  4.  
  5. [DEMO] Hello world. 
  6.  
  7. ../../base/startup/services/bootstrap_lite/source/system_init.c 40  

仔细看我加的打印语句,确实是在 38 行执行 MODULE_INIT(run); 后才打印 [DEMO] Hello world.

所以跟我们猜测的一样。当然没完,我们得分析为啥 是这样。

 

2. 链接

我们看下 MODULE_INIT(run); 做了什么。事实上,它只是一个宏。

  1. #define MODULE_INIT(name) \ 
  2.  
  3. do { \ 
  4.  
  5. MODULE_CALL(name, 0); \ 
  6.  
  7. } while (0) 

而 MODULE_CALL(name, 0); 又可以展开:当然里面的if 语句的打印是我后面加的。

 

我们可以看到 它其实是定义了一个 InitCall 指针,然后指针是这个:

  1. (MODULE_BEGIN(name, step)) 

而 MODULE_BEGIN 宏其实展开后如下:

  1. #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 代码段,里面存放着函数指针。

 

好了,到这里大家应该都明白了吧,继续看这个图,其实 这里只不过是把这个代码段里面的所有函数指针都取出来,然后执行一下函数指针指向的函数。 

 

聪明的你应该就猜到了,.zinitcall.run2.init 代码段里面的函数指针,指向的就是 HelloWorld 函数了~~~

 

到了这里就剩下最后一个问题了: 怎么让它指向 HelloWorld 函数。

这里其实就是 SYS_RUN的功劳了。

我们也来看SYS_RUN做了什么,其它也是一个宏,展开过程如下:

我们可以看到,其实SYS_RUN(HelloWorld) 其实最终结果就是:

  1. static const InitCall USED_ATTR __zinitcall_##layer##_##func \ 
  2.  
  3.         __attribute__((section(".zinitcall." clayer #priority ".init"))) = HelloWorld 

看起来很复杂,我们不乏拆解一下:

 

我们先不看红色字体部分,那么结果就是:

static const InitCall = HelloWorld

是不是很简单,其实就是定义了一个全局变量(函数指针),它指向 HelloWorld 。

那红色字体是做什么用呢?它其实就是告诉编译,我这个变量(static const InitCall 变量),很特殊,编译的时候给我编译在 .zinitcall.run2.init 段。

到了,一切都明了了,最后来一张启动流程图总结一下: 

 

3. 忠告

这里有两个忠告:

1、请不要直接在SYS_RUN()定义的入口函数直接写 while(1)

——这个很简单理解了,因为系统启动后,app_main会调用到 我们定义的SYS_RUN()定义的入口函数,比如HelloWorld。 如果我们在 HelloWorld 函数中写了while(1) 就会导致 app_main 后续的代码得不到执行,肯定有问题。

2、SYS_RUN()定义的入口函数创建的线程,请一定要有sleep动作。

为了解决第一个问题,我们很自然地想到,可以在 SYS_RUN()定义的入口函数 创建线程,这样就可以while(1)了。哈哈,其实也是有问题,因为 app_main 本身也是一个任务,如果我们自己创建地任务优先级特别高,就会导致 app_main 任务不会被执行,还是有问题。所以要有sleep,确保 app_main 后续地代码能顺利执行下去。

想了解更多内容,请访问:

51CTO和华为官方战略合作共建的鸿蒙技术社区

https://harmonyos.51cto.com/#zz

【编辑推荐】

  1. 机器人也开始"怕疼"了,无需人工干预即可"自愈"
  2. 这几个 IPv6 问题,你懂了吗
  3. 1024程序员节:一切都有根本原因
  4. Python中的yield到底是个什么鬼?
  5. 使用Terraform在AWS上部署Jenkins
【责任编辑:jianghua TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

云原生架构实践

云原生架构实践

新技术引领移动互联网进入急速赛道
共3章 | KaliArch

27人订阅学习

数据中心和VPDN网络建设案例

数据中心和VPDN网络建设案例

漫画+案例
共20章 | 捷哥CCIE

189人订阅学习

搭建数据中心实验Lab

搭建数据中心实验Lab

实验平台Datacenter
共5章 | ITGO(老曾)

119人订阅学习

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO官微