#2020征文-开发板#SYS_RUN()和MODULE_INIT()之间的那些事
接触鸿蒙设备开发有一段时间了,也是时候好好挖一挖鸿蒙设备程序的启动流程了。
破冰问题:鸿蒙设备程序从哪里开始运行的?
相信大家都已经非常清楚了,鸿蒙设备程序需要指定入口函数,具体表现在代码层面就是通过语句 SYS_RUN(app_entry); 指定,其中 app_entry 是设备程序入口函数名;而整个鸿蒙设备的启动流程也可以顺理成章的挖掘出来。如下图:

这看起来非常完美了,解决了所有问题!可是,我觉得还是有不清楚的地方,即:MODULE_INIT(run) 干了什么事?为什么最终会调用到 app_entry() 这个入口函数?
接下来,我们逐个问题的解决!
本质问题:MODULE_INIT(run) 干了什么事?
要弄清楚这个问题,就得先来讲讲 SYS_RUN() 究竟是什么?!有同学可能会认为 SYS_RUN(app_entry); 是一个函数调用语句,将设备程序入口地址注册到系统中,进而调用。从原理上这么理解没错,可细节上根本不是那么回事! SYS_RUN() 在用法上很像函数,但本质是一个宏!必须强调: 在 C 语言中无法在函数之外进行函数调用,而 SYS_RUN(app_entry); 出现的位置并不在任何函数中,所以它不可能是函数调用。那会是什么呢?真相只有一个,只可能是一个定义(声明)语句。为了证明这个结论,我们将 SYS_RUN() 这个宏彻底扒光了看个透彻。如下:


最终,我们可以知道:SYS_RUN(app_entry); 是定义了一个名为 __zinitcall_run_app_entry 的函数指针,其类型是 InitCall,无论是否使用都不会编译报错,并且强制编译使其最终存放在名为 .zinitcall.run2.init 的段中。
好!接下来就可以直接分析 MODULE_INIT(run) 了。


MODULE_INIT(run) 展开之后根本看不出和 app_entry 有任何关系啊!我们用了九牛二虎之力把宏掰开了,可结果貌似一无所获!!!MODULE_INIT() 和 SYS_RUN() 之间的关系还是非常不明朗,接下来该怎么办呢?
仔细观察这两个宏拼接出来的符号!

可以发现它们都和 zinitcall 有关,并且我们也知道了 SYS_RUN(app_entry) 定义的全局指针就放在名为 .zinitcall.run2.init 的段中,所以可以推测:这个两个宏的关系是通过链接脚本关联的。
接下来,通过工具查看目标文件的段信息和符号信息。

通过输出可以知道,在名为 .zinitcall.run2.init 的段中确实存在 __zinitcall_run_app_entry 这个符号。
之后,动手翻源码。。。。
经过努力,我们可以找到 \code-1.0\vendor\hisi\hi3861\hi3861\build\build_tmp\scripts\link.lds 文件,并且发现如下的脚本代码:

这样就真相大白了:
SYS_RUN(app_entry) 定义的函数指针 __zinitcall_run_app_entry 通过强制编译的方式进入 .zinitcall.run2.init 段中。在链接脚本中定义的两个符号 __zinitcall_run_start (理解为数组名)和 __zinitcall_run_end 分别指向 __zinitcall_run_app_entry 所在数据段的起始位置和结束位置。 又因为 MODULE_INIT(run) 的功能就是遍历 __zinitcall_run_start 和 __zinitcall_run_end 所指定的区域(理解为函数指针数组),并调用每个单元(指针)所指向的函数,因此,__zinitcall_run_app_entry 所指向的函数必然被调用,即:app_entry() 必然被调用。
更进一步阅读这个链接脚本可知:目标文件中的 .zinitcall.run2.init 段最终会被链接并汇编进一个名为 .zInit 的数据段中!
查看最终可执行程序中的符号信息和段信息可证明这个结论。

最终可执行程序中存在 __zinitcall_run_app_entry , __zinitcall_run_start 以及 __zinitcall_run_end , 并且 __zinitcall_run_app_entry 和 __zinitcall_run_start 的地址均为 0x004aeb1c,根据输出的段信息可知,它们均位于 .zInit 段中。证毕!
总结:
通过强制编译链接构成一个全局指针数组(每个 SYS_RUN() 定义一个数组元素)
在链接脚本中定义符号自动确认这个数组的起始地址和结束地址
MODULE_INIT() 通过遍历的方式调用数组元素所指向的函数
PS:
大家如果感兴趣可以自己亲手动手实验一下,所需材料和工具可在文末附件中下载。
1)编译附件中的 hello_world 工程(基于Hi3861)
2)将下面编译得到的目标文件拷贝到工具目录
\code-1.0\out\wifiiot\obj\applications\sample\wifi-iot\app\hello_world\hello_world.o
\code-1.0\out\wifiiot\Hi3861_wifiiot_app.out
3)执行命令观察结果
./nm hello_world.o
./objdump -h hello_world.o
./nm Hi3861_wifiiot_app.out
./objdump -h Hi3861_wifiiot_app.out
感叹一下,这真是一个精妙绝伦的设计方案!这样设计,理论上支持任意多的设备程序,开发者只需要简单的指定程序入口即可,完全不用关心背后的机制,也不用担心最多支持多少程序的问题。
这一招,学到了!!!
文中涉及多个进阶知识点,可参考狄泰软件学院《C语言进阶剖析教程》和《唐老师的私房课》。
作者:唐佐林
想了解更多内容,请访问: 51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com
#2020征文-开发板#SYS_RUN()和MODULE_INIT()之间的那些事的更多相关文章
- #2020征文-开发板# 用鸿蒙开发AI应用(二)系统篇
目录: 前言 安装虚拟机 安装 Ubuntu 设置共享文件夹 前言上回说到,我们在一块 HarmonyOS HiSpark AI Camera 开发板,并将其硬件做了一下解读和组装.要在其上编译鸿蒙系 ...
- #2020征文-开发板# 用鸿蒙开发AI应用(三)软件篇
目录: 前言 HarmonyOS 简介 DevEco Device Tool(windows下) 获取源码(切换到ubuntu) 烧录程序(切换回windows) 前言上一篇,我们在 Win10 上用 ...
- #2020征文-开发板# 用鸿蒙开发AI应用(一)硬件篇
目录: 前言 开发板简介 产品特色及功能 产品参数 各个主板功能简介 Hi3516DV300 芯片手册 前言鸿蒙2.0的系统刚开源出来,华为志在打造1+8+N万物互联的全场景智慧生活,不仅是国产操作系 ...
- #2020征文-开发板#使用Python开发鸿蒙应用--2021.01.07直播图文
写在前面: 每年的过年前夕,手中的项目一定会告急...而自己又缺乏三头六臂七十二变等特技,所以只能在鸿蒙社区先消失一阵子了.今天再看社区的帖子,发现大家的进步可不一般,各种案例示例层出不穷,一片欣欣向 ...
- #2020征文-开发板# 用鸿蒙开发AI应用(五)HDF 驱动补光灯
目录: 前言 硬件准备 HDF 驱动开发 总结 前言上一篇,我们在鸿蒙上运行了第一个程序,这一篇我们来编写一个驱动开启摄像头的红外补光灯,顺便熟悉一下鸿蒙上的 HDF 驱动开发. 硬件准备先查一下原理 ...
- 建立开发板与PC机之间的nfs服务器
ARM开发板与PC通过NFS 网络文件系统挂接,很方便文件的移动,也为我们的开发带来了方便.接下来,我就详细的介绍下如何建立ARM与PC机的挂接. 我是用的UBuntu系统. 首先,我们需要在主机上安 ...
- 虚拟机 开发板 PC机 三者之间不能ping通的各种原因分析
这个问题事实上也相对照较简单.可是非常多网友都给我发消息说 遇到不能ping,每一个人都得回答一次确实显得心有余而力不足.如今我对遇到这几种问题给出最完整的解决方式. (说实话基本上也仅仅要这几种可能 ...
- 主机、虚拟机、开发板(u-boot)之间的连接 - ping测试
1.设置主机的IP地址(这里注意,设置一定要设置网线宽带IP,不要选成无线网络的) 查看重点是否本地以太网卡(Realtek PCIe……) 2.修改本地连接3个IP地址,一定主机.虚拟机.开发板 三 ...
- 开发板与pc之间文件传输:kermit and lrzsz
imx6开发板与pc机之间通过串口传输文件步骤: 1. 安装好kermit并可以使用 2. 交叉编译lrzsz开源软件并把可执行程序lrz lsz拷贝到开发板 2.1 下载并解压lrzsz-0.12. ...
随机推荐
- kafka常见面试题
1.Kafka 中的 ISR(InSyncRepli).OSR(OutSyncRepli).AR(AllRepli)代表什么? 1.AR = ISR+OSR ISR: kafka 使用多副本来保证消息 ...
- python之路《模块》
1.time模块 FUNCTIONS asctime(...) asctime([tuple]) -> string Convert a time tuple to a string, e.g. ...
- kettle练习
Kettle实现,把数据从CSV文件复制到Excel文件. 首先,创建一个转换,找到核心对象,找到输入里面的CVS文件输入图元,拖拽到工作区域,双击CVS文件输入. 可以修改步骤的名称,点击浏览,选择 ...
- 最全Linux搭建SVN服务端教程
文章首推 支付宝接口对接 高德地图调用 验证码登录 QQ邮箱登录 今日主题:Linux搭建SVN服务端 简介 相信程序员对SVN还是不陌生的,虽然现在用Git比较多,但是SVN也是用的,SVN可以做代 ...
- new Date在不同浏览器识别问题
原文 http://www.cnblogs.com/yangxiaopei/articles/4708157.html 4. 正确的做法 以下列出正确的做法: var time1 = (timesta ...
- python中的unittest库
1.unittest框架是python自动化测试的入门框架,它是python自带的一个模块 包含以下几个子模块 测试用例:TestCase 测试集:TestSuite 加载用例:TestLoader ...
- 关于Java里方法重载
覆盖和重载很重要,并且比较容易混淆,所以面试中常见.基础回答:覆盖(Override),又叫重写,是指子类对父类方法的一种重写,方法名.参数列表必须相同,返回值小于父类,只能比父类抛出更少的异常,访问 ...
- redis cluster可用性测试
上一节,我们用三台redis组成了cluster,现在我们停掉一台试试: 比较奇怪的是,在停掉其中一台服务器之前建立的链接仍然可以正常执行命令,当我们断开重连时,命令就都被拒绝了: 关联知识: 什么时 ...
- 【线程池】toString
java.util.concurrent.RejectedExecutionException: Task com.personal.practice.jvm.Jstacktest$1@7d605a5 ...
- 心跳event
在通过sessionID和passwd获取会话的时候会使原会话断开,后续的事件都变为disconnected,且zk会不断发送disconnected给原连接,connected给新连接