#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. ...
随机推荐
- TypeScript注意
中文文档中,元组目前已经不能越界访问
- PyCharm离线安装PyQt5_tools(QtDesigner)
目录 下载所需的whl包 安装whl 配置PyCharm 测试 下载所需的whl包 打开链接 PyPI,依此搜索 python_dotenv,PyQt5_sip,PyQt5,pyqt5_tools:基 ...
- 如何测量Ceph OSD内存占用
前言 这个工具我第一次看到是在填坑群里面看到,是由研发-北京-蓝星同学分享的,看到比较有趣,就写一篇相关的记录下用法 火焰图里面也可以定位内存方面的问题,那个是通过一段时间的统计,以一个汇总的方式来查 ...
- RedisUtils工具类
package test.util; import org.springframework.beans.factory.annotation.Autowired; import org.springf ...
- Flink处理函数实战之四:窗口处理
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- php(tp5)生成条形码
因为公司业务需要,研究了一下条形码 1.下载barcodegen扩展包 官网地址:https://www.barcodebakery.com 2.下载完后解压至 extend 文件夹里面,然后复制以下 ...
- Folx种子下载器怎么管理下载任务
对于喜欢追剧的用户来说,同时下载好几部剧是司空见惯的事情.但有时候,有些剧比较好看或者热度比较高时,就会希望优先将其下载下来. 对于使用Folx种子下载器的用户来说,可以结合使用下载列表+最大活动数的 ...
- guitar pro系列教程(十四):Guitar Pro教程之创建新乐谱后的设置
前面的章节我们有对Guitar Pro的单个功能作介绍,对于初学作曲,且又是吉他初学者的朋友们来说,学完这些功能介绍,自己还不能融会贯通起来,创建了一个新的乐谱后,但是看起来还不是很满意,今天我们就创 ...
- js 时间日期与时间戳之间转换
1 1.将时间(2017-08-10)转换时间戳 2 var startTime = '2017-08-10'; 3 var startdate = new Date(Date.parse(start ...
- python菜鸟教程学习9:函数
函数的定义 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段.python提供了很多内建函数,但我们依然可以自己创建函数,叫做用户自定义函数. 自定义函数 你可以定义一个由自己想要功能 ...