架构编译器的感悟

这所谓的编译器就是将c语言代码编译为机器代码的,先将C编译为汇编代码,再由汇编器将汇编代码编译为机器代码,CPU执行的是机器代码编程


忽然发觉好像不少书都这么说,不少人也这么说,因而很天然的记住了,可是,我忽然想起了,这但是隐藏着一些道理。网络


1,C编译为汇编,对于这个过程,应该是平台无关的,具体是怎么实现的?这个是由C编译器开发商来处理,总之,若是我用IAR ARM的话,那么一样的main函数,编译出来应该是获得 ARM 格式的汇编代码,也就是说,使用的是 ARM 指令集,一样道理,若是是 GCC,也产生相似的,不过由于他们的内部实现并不同,因此出来的结果可能有差别,不过最后的结果是一致的,也就是获得对应平台的汇编代码。架构


2,汇编器将汇编代码翻译为机器代码,其实这个过程比较简单的,有点像查表,通常厂家都会免费提供这么一个编译器,不然MCU就不用卖了,通常C编译都会本身实现一个。这让我想起了,什么是架构 Architecture,一个CPU怎么寻址,指令集是什么,堆栈指针是怎么变化的,PC程序指针是怎么变化的,怎么取指令,这些东西都是每一个CPU区别于其余CPU所特有的东西,也就是一个CPU设计的时候所面临的内容。这就是架构,还记得学校学MCS-51的时候说得最多的是什么么?对,寻址和指令集,这个就是架构,老师想你学51这种架构,而不单纯是学怎么用这个芯片。函数


3,再说回上层的,ARM是一个核心,是一个架构,他制定了指令集,他指定了SP指针的访问方式,他指定了怎么寻址,因此说“ARM架构”学习

而后不少厂家根据ARM架构生产的芯片,由于你就一个核心没用啊?他就会执行指令,其余什么都不懂,咱们须要外设,须要不少外围器件,例如I2C,USB什么的,和ARM这个核心一同构成一个完整意义上的芯片,咱们叫 SOC (system on chip)单片系统,由于这个系统已经具有了基本跑起来的条件了,再在外围加上适当的复位和晶振电路,那基本就没有什么问题了。翻译


从这我又想到了,那既然是同一个核心出来的,每一个厂家生产芯片有什么不一样呢?其实说白了就是外围器件的不一样,例如ARM9芯片,三星的S3C2440,核心是 920T集成了LCD控制器,NAND FLASH 控制器,然而 Atmel的 AT91RM9200,核心一样是 ARM架构的 920T,可是他没有LCD控制器,却集成了网络方面的功能,这样看来,在一个架构上面不一样厂家添加不一样的功能器件,而获得了各类各样的芯片,也就是SOC了。设计


继续联想,不一样的SOC之间其实还真的没有什么本质性的差异,第1,2点也说明了,C是万国语言,他到底也是通用的,因此同一段MAIN函数,若是你什么都不写,那么编译出来,在每个soc都跑得通。而为何基于这个芯片开发的程序就不能用到拿个芯片呢?其实一个重要的问题就是地址的访问,寻址的问题。ARM 32位总线固定了 4GB的寻址空间,可是ARM并无硬性的规定哪一个地址应该作什么,哪一个地址不能作什么,具体怎么作,是SOC厂商说了算,因此产生的差别就是,可能这个soc里面的uart模块地址为 0x50000000 ,而另一个厂商的soc的uart模块地址为 0x40000000 ,C语言的器件编程,说白了不就是对某个地址放某些数据,例如我要设置UART,很简单,将正确的数据送到正确的地址,也就是UART模块所在的地址,那么一切都完工了。指针


这里体现了很重要的一点,就是所谓的编程就是在什么地址放什么数据或者要什么数据。从这个抽象层面来看,编译器能够很轻松的编译同一个核心的不一样SOC,由于不一样核心,那牵涉到架构的问题,要修改编译器内部的实现代码,可是同一个架构同一个核心的,对不一样芯片的访问的区别,就在于不一样的地址!!ip


若是对S3C2440的编程,核心是 ARM 920T ,咱们应该重点学习什么?开发

应该学习通用的,若是理解上面的内容,就知道什么是通用的了。那就是架构,搞懂了ARM 920T的架构,那么你使用的几个芯片又有什么差异呢?最多就是对着那个新的功能模块研究一下怎么设置。


4,再谈谈编译器

其实编译器实现的目的都是一个,机器代码,只是实现的办法和过程并不相同而已。那么对于众多的编译器,咱们应该选择哪一个?其实选择哪一个都没有所谓,他们都一样的强大,ARMCC, GCC, IAR KEIL-MDK都一样强大,只要你学会了用,总能知足你的须要。可是,是否是每一个编译器就各自为政呢?那倒不必定,因此呢,学习,是须要抓住核心

什么是核心?有几点

1)main函数执行以前究竟作了些什么

可能在学校不少老师都教,C语言的入口在main函数,全部的代码从这里开始!可是真的吗?其实并非的,main函数执行以前须要必要的环境,这个环境由谁来提供?其实,在你的C语言程序链接的时候,一个初始化的模块(通常是叫 crt0.o ,也就是 c running time的缩写)已经悄悄的被链接进去了。这是每一个编译器都会作的,关键是,这个crt0 的模块是编译器默认提供的仍是你本身去实现。若是是编译器自带的,那么你应该怎么去作部分修改以达到本身的目的,若是是本身写,那又应该怎么去写?这就是学习的重点了。

初始化模块主要的工做:设置栈,设置堆(heap,主要是为C库的malloc服务),.data的搬移(若是须要的话),.bss段的初始化,跳转到 main函数。这个就是共性,不管什么编译器,都是在作这几步而已,不一样的只是用各个编译器各自的语法去实现,可是本质也是不变的,固然,你也能够本身去实现,在链接的时候链接进来就OK了。

2)怎么处理链接(link)

编译的过程基本不须要去关心,编译器都作得好好的,你也没有干预的余地,最多就是传递一些不一样的编译参数,这个稍微看看自带的文档,或者干脆用到的时候再翻,也问题不大。真正的大问题出在链接。

编译的过程获得的obj文件,是彻底和地址无关的,例如,每一个源文件对应的obj文件,地址都是从0开始的,它真正链接的时候才由链接器linker分配真正运行时候的地址,因此,你要想处理每一个具体的代码段运行的时候应该在什么地方,就要注意学习link了。

其中指导linker工做的就是对应的脚本 linker scrpit,链接脚本,一样,这是一个共性,每一个编译器都确定有,只是实现办法的不一样,脚本具体的编写的语法不一样,因此这个是应该重点研究的,让你的代码听听话话,就花多点功夫去研究这个吧。


因此,每遇到一个编译器,先了解他怎么初始化,而后研究它怎么链接,了解了以后基本上你就会用这个编译器了,剩下的就不用说了,上课老师也说得够多了,怎么从main函数开始写,云云。

相关文章
相关标签/搜索