当前位置: 首页 > 科技观察

2020征稿-鸿蒙开发板SYS_RUN()和MODULE_INIT()之间的那些事儿

时间:2023-03-20 13:43:06 科技观察

更多内容请访问:与华为官方共建的鸿蒙技术社区https://harmonyos.51cto.com/#zz接触鸿蒙设备开发有一段时间了,是时候深挖一下鸿蒙设备程序的启动过程了。破冰问题:鸿蒙设备程序从哪里开始运行?相信大家都很清楚,鸿蒙设备程序需要指定入口函数,具体体现在代码层面,通过指定语句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,无论是否使用,都不会编译报错,强制编译,使其最终存放在name的段中。zinitcall.run2.init。好的!然后就可以直接分析MODULE_INIT(run)了。MODULE_INIT(run)展开后,我们完全看不出和app_entry有什么关系!我们动用了九牛二虎之力破解宏,结果好像什么都没有!!!MODULE_INIT()和SYS_RUN()的关系还是很不清楚,接下来怎么办?仔细观察这两个宏拼接出来的符号!可以发现它们都和zinitcall有关,我们也知道SYS_RUN(app_entry)定义的全局指针放在name里面。在zinitcall.run2.init这一段,可以推测这两个宏之间的关系是通过链接脚本关联的。接下来通过工具查看目标文件的段信息和符号信息。从输出中可以看出,符号__zinitcall_run_app_entry确实存在于名为.zinitcall.run2.init的部分中。之后,手翻源码。...经过努力,我们可以找到\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.out3)执行命令观察结果./nmhello_world.o./objdump-hhello_world.o./nmHi3861_wifiiot_app.out./objdump-hHi3861_wifiiot_app.out唉,这真是一个精妙的设计!通过这种设计,理论上可以支持任意数量的设备程序。开发者只需要简单的指定程序入口,无需关心背后的机制,更不用担心最多支持多少个程序。这个技巧我学会了!!!更多内容请访问:与华为官方共建的Harmonyos技术社区https://harmonyos.51cto.com/#zz【编辑推荐】2021年值得关注的7大安全趋势什么是RDB和AOF?在本文中了解Redis持久化!洞察:思考VUCA时代的数字化人才管理聊天杀人真的那么简单吗?数据时代将刷新你的认知