MDK的编程过程和变量存储位置

1 参考书籍《零死角玩转STM32-F429》

2 编程过程

  首先我们简单了解下 MDK 的编译过程,它与其它编译器的工作过程是类似的,该过程见图 51-1。

MDK的编程过程和变量存储位置_第1张图片

  (1) 编译, MDK 软件使用的编译器是 armcc armasm,它们根据每个 c/c++和汇编源文件编译成对应的以“.o”为后缀名的对象文件(Object Code,也称目标文件),其内容主要是从源文件编译得到的机器码,包含了代码、数据以及调试使用的信息;

(2) 链接,链接器 armlink 把各个.o 文件及库文件链接成一个映像文件“.axf”或“.elf”:

(3) 格式转换,一般来说 Windows Linux 系统使用链接器直接生成可执行映像文件 elf后,内核根据该文件的信息加载后,就可以运行程序了,但在单片机平台上,需要把该文件的内容加载到芯片上,所以还需要对链接器生成的 elf 映像文件利用格式转换器fromelf 转换成“.bin”或“.hex”文件,交给下载器下载到芯片的 FLASH ROM

3 程序的组成,存储和运行

  根据每一个程序的编译提示,如下图

MDK的编程过程和变量存储位置_第2张图片

  在工程的编译提示输出信息中有一个语句“Program SizeCode=xx RO-data=xx RWdata=xx ZI-data=xx”,它说明了程序各个域的大小,编译后,应用程序中所有具有同一性质的数据(包括代码)被归到一个域,程序在存储或运行的时候,不同的域会呈现不同的状态,这些域的意义如下:


Code:即代码域,它指的是编译器生成的机器指令,这些内容被存储到 ROM 区。


RO-dataRead Only data,即只读数据域,它指程序中用到的只读数据,这些数据被存储在 ROM 区,因而程序不能修改其内容。例如 C 语言中 const 关键字定义的变量就是典型的 RO-data

RW-dataRead Write data,即可读写数据域,它指初始化为“非 0 值”的可读写数据,程序刚运行时,这些数据具有非 0 的初始值,且运行的时候它们会常驻在RAM 区,因而应用程序可以修改其内容。例如 C 语言中使用定义的全局变量,且定义时赋予“非 0 值”给该变量进行初始化

ZI-data: Zero Initialie data,即 0 初始化数据,它指初始化为“0 值”的可读写数据域,它与 RW-data 的区别是程序刚运行时这些数据初始值全都为 0,而后续运行过程与 RW-data 的性质一样,它们也常驻在 RAM 区,因而应用程序可以更改其内容。例如 C 语言中使用定义的全局变量,且定义时赋予“0 值”给该变量进行初始化(若定义该变量时没有赋予初始值,编译器会把它当 ZI-data 来对待,初始化为 0);

ZI-data 的栈空间(Stack)及堆空间(Heap):在 C 语言中,函数内部定义的局部变量属于栈空间,进入函数的时候从向栈空间申请内存给局部变量,退出时释放局部变量,归还内存空间。而使用 malloc 动态分配的变量属于堆空间。在程序中的栈空间和堆空间都是属于 ZI-data 区域的,这些空间都会被初始值化为 0 值。编译器给出的 ZI-data 占用的空间值中包含了堆栈的大小(经实际测试,若程序中完全没有使用 malloc 动态申请堆空间,编译器会优化,不把堆空间计算在内)

  程序组件所属的区域

机器代码指令 Code
常量 RO-data
初值非0的全局变量 RW-datra
初值为0的全局变量 ZI-data
局部变量 ZI-data栈空间
使用malloc动态分配的空间 ZI-data堆空间


  4 程序的存储与运行

  RW-data ZI-data 它们仅仅是初始值不一样而已,为什么编译器非要把它们区分开?这就涉及到程序的存储状态了,应用程序具有静止状态和运行状态。静止态的程序被存储在非易失存储器中,如 STM32 的内部 FLASH,因而系统掉电后也能正常保存。但是当程序在运行状态的时候,程序常常需要修改一些暂存数据,由于运行速度的要求,这些数据往往存放在内存中(RAM),掉电后这些数据会丢失。因此,程序在静止与运行的时候它在存储器中的表现是不一样的。

MDK的编程过程和变量存储位置_第3张图片

  图中的左侧是应用程序的存储状态,右侧是运行状态,而上方是 RAM 存储器区域,下方是 ROM 存储器区域。程序在存储状态时, RO 节(RO section)及 RW 节都被保存在 ROM 区。当程序开始运行时,内核直接从 ROM 中读取代码,并且在执行主体代码前,会先执行一段加载代码,它把 RW 节数据从 ROM 复制到 RAM, 并且在 RAM 加入 ZI 节, ZI 节的数据都被初始化为
0。加载完后 RAM 区准备完毕,正式开始执行主体程序。编译生成的 RW-data 的数据属于图中的 RW 节, ZI-data 的数据属于图中的 ZI 节。是否需要掉电保存,这就是把 RW-data 与 ZI-data 区别开来的原因,因为在 RAM 创建数据的时候,默认值为 0,但如果有的数据要求初值非 0,那就需要使用 ROM 记录该初始值,运行时再复制到 RAM。
  STM32 的 RO 区域不需要加载到 SRAM,内核直接从 FLASH 读取指令运行。计算机系统的应用程序运行过程很类似,不过计算机系统的程序在存储状态时位于硬盘,执行的时候甚至会把上述的 RO 区域(代码、只读数据)加载到内存,加快运行速度,还有虚拟内存管理单元(MMU)辅助加载数据,使得可以运行比物理内存还大的应用程序。而 STM32 没有 MMU,所以无法支持 Linux 和 Windows 系统。
  当程序存储到 STM32 芯片的内部 FLASH 时(即 ROM 区),它占用的空间是 Code、RO-data 及 RW-data 的总和,所以如果这些内容比 STM32 芯片的 FLASH 空间大,程序就无法被正常保存了。当程序在执行的时候,需要占用内部 SRAM 空间(即 RAM 区),占用的空间包括 RW-data 和 ZI-data。应用程序在各个状态时各区域的组成见表 。

MDK的编程过程和变量存储位置_第4张图片

  MDK 中,我们建立的工程一般会选择芯片型号,选择后就有确定的 FLASH SRAM 大小,若代码超出了芯片的存储器的极限,编译器会提示错误,这时就需要裁剪程序了,裁剪时可针对超出的区域来优化。


5 后记

  STM32F1系列因为没有MMU,所以不能跑操作系统。但是比如三星的S3C64310,在其数据手册上可以看到这么一句话:It also includes a full MMU to handle virtual memory management. 这说明S3C6410是具备MMU的,可以管理虚拟内存。






















你可能感兴趣的:(STM32)