分类:
2012-02-20 20:09:57
原文地址:程序结构分析和理解 作者:gongping11
最近的一段时间又温习了一下嵌入式开发的基本工具和程序的一些基本结构,逐渐脱离了机械学习的过程,在学习的过程中了解到自己需要了解的东西还很多。记得我在大学期间学习DSP,当时的任务是完成2048点数据的FFT变换,记得当时程序(C语言)完成很快,但是在后期总是不能出现正确的效果,最后发现原来是CMD(配置)文件存在问题,通过老师的讲解以及自己的学习了解到了程序的基本结构,也就是所谓的代码段,数据段等,以及具体的存储器管理问题。这些都是链接器的基本功能。从此也就知道了程序的一些特点。
1、目标文件的构成
二进制文件是由一系列的段构成的,当然也会存在一些符号,存储器分配等等,从链接文本其实就能大概知道程序的组成,各个目标文件的同一段结合起来就实现了可执行程序的链接,当然具体的链接方式和原则都是工具设计好的。比如链接的顺序按着输入的顺序等。
记得在移植u-boot到TQ2440的过程中,曾经就修改过链接文本,也就是u-boot.lds文件,当时因为自己添加了一个关于nand flash 操作的函数,为了保证添加的函数不会在链接的过程中将启动代码之前而导致芯片启动存在问题,我特意修改了程序的链接顺序,保证链接文档正确链接。
1. [root@Gong-Computer u-boot-2010.06]# vi arch/arm/cpu/arm920t/u-boot.lds
2. . = ALIGN(4);
3. .text :
4. {
5. arch/arm/cpu/arm920t/start.o (.text)
6. board/samsung/smdk2440/lowlevel_init.o (.text)
7. board/samsung/smdk2440/nand_read.o (.text)
8. *(.text)
9. }
关于二进制文件中的基本内容主要是包括几个段的。当然还需要其他的一些条件,在链接的过程中,好像还需要一些C语言运行的环境,主要是用来控制程序的启动和关闭。这些都是crt*(C RunTime)目标文件实现的。这些目标文件在我们程序设计中不经常看到,如果分析过u-boot的编译过程就会发现,这些目标文件确实存在。
链接文本使得文件的链接更加的方便和实用。因此了解目标的最基本段落是非常的必要的。
在其中需要注意的几点:
1、静态变量不管是局部还是全局的都是在数据段中,而不想局部变量在堆栈中,当栈弹出以后,其中的内容也就释放了,静态变量不会改变,但是对于局部静态变量则只能被定义该变量的函数访问,不能被其他的函数访问。
2、全局变量、静态变量如果被初始化为0,或者没有被初始化,则该变量被分配到.bss(未初始化部分),只有当全局变量和静态变量初始化为非零数值时才会分配到.data段中。
3、全局变量和静态变量如果没有被初始化一般都会默认为0,这也是为什么将这两个数初始化为0,仍然处在.bss段的原因。因此需要注意全局变量和静态变量在没有初始化时的值,但是对于局部变量则没有这个特点,如果没有初始化则会出现一个随机值。
以上的结论可以通过代码进行手动测试。
2、Linux中程序结构
在Linux中每一个进程都存在一个4G的虚拟空间,其中前3G空间是用户空间,而后1G空间则是内核空间,这4G的空间在各个进程之间都是相互独立的。但是这些内存空间的区域分配确实相同的,而且各个区域的起始地址也是固定好的,Linux程序的结构如下:
由于各个段的起始地址都是固定的,这样就便于虚拟地址到物理地址的映射,方便了程序的加载。特别是共享库的实现。
3、堆栈和堆
其实我对这两个概念在刚开始的时候也存在很大的误解,不明白其中的关系,总是把堆栈理解成堆和栈,实质上堆栈就是指栈,搞清楚这个,两者的区别就容易理解啦。堆栈其实也可以认为是一种数据结构,典型的先进后出特点。
一般而言堆栈都是反向增长的,也就是所谓的从高地址到低地址的增长方式,但是也有其他的增长方式,比如ARM有4种不同的增长模式,所以堆栈的增长方向只能依据CPU而言,不同的CPU其堆栈可能存在一定的差别。堆栈主要用来实现函数的调用。
堆一般而言都是在bass段的上面,主要用来实现动态内存的分配和释放,在C语言中主要是通过malloc/free函数实现,在C++中则主要采用new/delete实现。但是对于这一块的内存通常会不断的分配和释放以及满足基本的对齐形式,这样就导致了内存碎片的产生,减小了访问的速率。同时因为在这段区域运行零空间申请,以及释放后指针仍然有效等问题,所以在释放完毕以后通常将指针指向NULL。