STM32
登录
直播中
吕珠峰
10年用户
797经验值
擅长:可编程逻辑
私信
关注
[问答]
浅析__main函数的原理及其作用
开启该帖子的消息推送
启动代码
函数
__main函数是什么?
__main函数的作用主要有哪几点?
回帖
(1)
黄登高
2021-11-25 11:52:24
1、__main 作用
__main函数是C/C++运行时库的一个函数,嵌入式系统在进入应用主程序之前必须有一个初始化的过程,使用__main标号引导系统时必须将应用程序的入口定义为main()。
在初始化的过程中,__main函数的作用主要有两点:
1) 完成对映像文件的初始化操作
a、映像文件
链接器把多个目标文件链接成一个映像文件。
b、加载地址和执行地址
映像文件可以有两种地址:加载地址和执行地址。
加载地址是映像文件在存储器中的存储地址;执行地址就是映像文件运行时的地址。
c、加载域和执行域
文件加载的存储区叫加载域,文件运行的存储区叫执行域。
d、从加载地址到执行地址
在结构比较简单的系统中,加载地址就是执行地址;
而在复杂系统中,程序运行前,常常会把映像文件的一部分或全部从存储区域移出去,此时执行地址就不再是加载地址。
知道以上几个概念,__main函数对映像文件的初始操作就不难理解了。
对于加载地址和执行地址不同的映像文件,__main函数会把加载地址的代码和数据复制到执行地址中,并且对被链接器指定为需要初始化为0的段,进行清零操作。
2) 调用__rt_entry函数,进入用户程序。
__rt_entry函数的运行流程如图:
2、进入主程序
当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序,即呼叫主应用程序。
最简单的一种情况是:
IMPORT mainB main 直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。
在ARM ADS环境中,还另外提供了一套系统级的呼叫机制。
IMPORT __mainB __main __main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到main()。
所以说,前者是库函数,后者就是我们自己编写的main()主函数;
因此我们用的B __main其实是执行库函数,然后该库函数再调用我们的main() 函数,因此在单步调试时会看到先要跑一段程序(其实是库函数),然后再单步到我们自己的main函数(这个同时也说明如果有B __main 则就对应必须有main函数,否则编译出错);
如果我们用 B main来进入我们的主函数的话,那在单步调试时就看到直接进入到我们自己的main函数了,中间不会看到其他程序;
那么用B __main和用B main 这两这进入我们的main函数方式有什么不同呢?
如果采用前者则会由编译器加入一段"段拷贝"程序,即我们说的从加载域到执行域转化程序;而采用后者就没有这个了;
因此如果要进行 "段拷贝"只能自己动手编写程序来实现了,完成段拷贝后就可以进入我们的主函数了,当然这个主函数不一定是叫做main(),可以起个其他好听的名字,这个有别于使用B __main方式;不管采用哪种方式进入我们的程序,都要有一段"段拷贝"程序,跑完了段拷贝后才能可以进入我们主程序了。
startup.s 这个文件并没有所谓的"段拷贝"功能。
对含有启动程序来说,"执行地址与加载地址相同"不容易实现:
如果执行地址与加载地址相同哪当然不需要做"段拷贝",但是个人理解编译器还会加入"段拷贝"程序(如果用B __main 的话),只是因为条件不满足而不执行而已;但是对含有启动程序来说,"执行地址与加载地址相同"就不容易了.因为启动程序是要烧到非易失存储器里,用来在上电执行的,而这个程序必定会有RW段,如果RW放在非易失存储器,如FLASH,那就不好实现RW功能了,因此要给RW移动到能够实现RW功能的存储器,如SRAM等.因此,对含有启动程序来说,"执行地址与加载地址相同"就不容易实现;程序的入口点在C 库中的__main 处,在该点,库代码执行以下操作:
1)将非零(只读和读写)运行区域从其载入地址复制到运行地址。
2)清零ZI 区域。
3)跳转到__rt_entry。
1、__main 作用
__main函数是C/C++运行时库的一个函数,嵌入式系统在进入应用主程序之前必须有一个初始化的过程,使用__main标号引导系统时必须将应用程序的入口定义为main()。
在初始化的过程中,__main函数的作用主要有两点:
1) 完成对映像文件的初始化操作
a、映像文件
链接器把多个目标文件链接成一个映像文件。
b、加载地址和执行地址
映像文件可以有两种地址:加载地址和执行地址。
加载地址是映像文件在存储器中的存储地址;执行地址就是映像文件运行时的地址。
c、加载域和执行域
文件加载的存储区叫加载域,文件运行的存储区叫执行域。
d、从加载地址到执行地址
在结构比较简单的系统中,加载地址就是执行地址;
而在复杂系统中,程序运行前,常常会把映像文件的一部分或全部从存储区域移出去,此时执行地址就不再是加载地址。
知道以上几个概念,__main函数对映像文件的初始操作就不难理解了。
对于加载地址和执行地址不同的映像文件,__main函数会把加载地址的代码和数据复制到执行地址中,并且对被链接器指定为需要初始化为0的段,进行清零操作。
2) 调用__rt_entry函数,进入用户程序。
__rt_entry函数的运行流程如图:
2、进入主程序
当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序,即呼叫主应用程序。
最简单的一种情况是:
IMPORT mainB main 直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。
在ARM ADS环境中,还另外提供了一套系统级的呼叫机制。
IMPORT __mainB __main __main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到main()。
所以说,前者是库函数,后者就是我们自己编写的main()主函数;
因此我们用的B __main其实是执行库函数,然后该库函数再调用我们的main() 函数,因此在单步调试时会看到先要跑一段程序(其实是库函数),然后再单步到我们自己的main函数(这个同时也说明如果有B __main 则就对应必须有main函数,否则编译出错);
如果我们用 B main来进入我们的主函数的话,那在单步调试时就看到直接进入到我们自己的main函数了,中间不会看到其他程序;
那么用B __main和用B main 这两这进入我们的main函数方式有什么不同呢?
如果采用前者则会由编译器加入一段"段拷贝"程序,即我们说的从加载域到执行域转化程序;而采用后者就没有这个了;
因此如果要进行 "段拷贝"只能自己动手编写程序来实现了,完成段拷贝后就可以进入我们的主函数了,当然这个主函数不一定是叫做main(),可以起个其他好听的名字,这个有别于使用B __main方式;不管采用哪种方式进入我们的程序,都要有一段"段拷贝"程序,跑完了段拷贝后才能可以进入我们主程序了。
startup.s 这个文件并没有所谓的"段拷贝"功能。
对含有启动程序来说,"执行地址与加载地址相同"不容易实现:
如果执行地址与加载地址相同哪当然不需要做"段拷贝",但是个人理解编译器还会加入"段拷贝"程序(如果用B __main 的话),只是因为条件不满足而不执行而已;但是对含有启动程序来说,"执行地址与加载地址相同"就不容易了.因为启动程序是要烧到非易失存储器里,用来在上电执行的,而这个程序必定会有RW段,如果RW放在非易失存储器,如FLASH,那就不好实现RW功能了,因此要给RW移动到能够实现RW功能的存储器,如SRAM等.因此,对含有启动程序来说,"执行地址与加载地址相同"就不容易实现;程序的入口点在C 库中的__main 处,在该点,库代码执行以下操作:
1)将非零(只读和读写)运行区域从其载入地址复制到运行地址。
2)清零ZI 区域。
3)跳转到__rt_entry。
举报
更多回帖
rotate(-90deg);
回复
相关问答
启动代码
函数
.extern
main
声明
main
函数
是必须的吗?
2019-04-17
2033
为什么
main
函数
里一般没有SystemInit
函数
呢
2021-11-29
1417
使用FRDM 16KEZ的项目,如果def没有得到trought 和 #if定义中的
函数
使用in
main
时不起
作用
怎么解决?
2023-04-17
321
请问
main
函数
中的参数
作用
是什么?
2019-03-20
1425
求论文《TD-LTE基站射频测试
浅析
》
2016-08-16
2693
请教IAR启动
main
函数
的流程是怎样的
2021-11-25
1542
一个系统里面,没有
main
函数
,而有'_mian'和'**
main
'几个
函数
是什么?
2020-06-05
1911
请问启动文件的
main
函数
的定义在哪里可以看到?
2019-06-19
2580
如何去实现单片机接口适配器模式的C语言
函数
呢
2022-02-25
786
为什么把
main
函数
的地址赋值给pc寄存器
2019-03-06
2067
发帖
登录/注册
20万+
工程师都在用,
免费
PCB检查工具
无需安装、支持浏览器和手机在线查看、实时共享
查看
点击登录
登录更多精彩功能!
首页
论坛版块
小组
免费开发板试用
ebook
直播
搜索
登录
×
20
完善资料,
赚取积分