STM32 - Keil MDK编译过程

普通 .C 源文件到可执行的过程:

  1. 分为:预处理(Preprocessing),编译(Compilation),汇编(Assemble),链接(Linking)。
  • 预处理:
    • 以“#”号开头的预处理指令,比如包含#include,宏定义指定#define等。在源程序中这些指令都放在函数之外,而且一般放在源文件的前面。使用预处理器把源文件(c/h)经过预处理生成xxx.i文件。(简单来说就是展开程序中的宏等)
  • 编译
    • 指将经过预处理文件(xxx.i)之后的程序转换成特定汇编(xxx.s)代码的过程。
  • 汇编
    • 汇编过程将上一步生成的汇编代码转换成机器码,这一步产生的文件叫做目标文件,是二进制格式。
  • 链接
    • 链接过程使用链接器将上述目标文件与其它目标文件、库文件、启动文件等链接起来生成可执行文件。其他目标文件包括静态连接库和动态连接库。

Keil MDK编译过程 :

Keil1

  • **编译:**Keil MDK 软件使用的编译器是 armcc 和 armasm,它们根据每个 c/c++ 和汇编源文件编译成对应的以 “ .o ” 为后缀名的对象文件 (Object Code,也称目标文件),其内容主要是从源文件编译得到的机器码,包含了代码、数据以及调试使用的信息。
  • **链接:**链接器 armlink 把各个 .o 文件及库文件链接成一个映像文件 “.axf” 或 “.elf” 。
  • **格式转换:**一般来说 Windows 或 Linux 系统使用链接器直接生成可执行映像文件 elf 后,内核根据该文件的信息加载后,就可以运行程序了,但在单片机平台上,需要把该文件的内容加载到芯片上,所以还需要对链接器生成的 elf 映像文件利用格式转换器 fromelf 转换成 “.bin”或“.hex”文件,交给下载器下载到芯片的 FLASH 或 ROM 中。

Keil2

程序组成:

Keil3

  1. 经过链接后代码分为4部分,分别为:Code、RO-data、RW-data、ZI-data
  • Code:代码区。指代程序中的代码,即函数体的大小,这些会被存储到ROM(Flash)中。注意程序中未使用的函数也会算在Code中,也即会占用FLASH空间,因此不用的函数最好删除掉,以免占用过多FLASH空间。
  • RO-data:Read Only data,即只读数据区,它指程序中用到的只读数据,这些数据被存储在ROM 区,因而程序不能修改其内容。例如 C 语言中 const 关键字定义的变量就是典型的RO-data,还有已经初始化的字符串等。
  • RW-data:Read 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的堆栈空间中。
  • 在 C 语言中,函数内部定义的局部变量属于栈空间,进入函数的时候从向栈空间申请内存给局部变量,退出时释放局部变量,归还内存空间。而使用 malloc 动态分配的变量属于堆空间。在程序中的栈空间和堆空间都是属于ZI-data 区域的,这些空间都会被初始值化为 0 值。编译器给出的 ZI-data 占用的空间值中包含了堆栈的大小。

Keil4

程序的静止状态和运行状态:

Keil5

  • 静止状态
    • 向Flash中下载程序时,其实仅仅下载的是CODE+RO-data+RW-data的内容,意思就是说,在掉电情况下,Flash里面的记忆体仅包含CODE+RO-data+ RW-data这三块。
  • 运行状态
    • 当上电后,程序运行时,首先程序会从特定的地址进行启动,启动时会将 RW-data 的数据载入到 SRAM 中(启动文件中有一段代码是干这件事的, ST 已经帮我们做完了这件事)。
    • 单片机的 RO 区域不需要载入到 SRAM ,内核直接从 FLASH 读取指令运行。
    • 那 ZI-data 的数据怎么办呢?对于初始值为 0 全局变量来说,因为要在 Code 区要调用该全局变量,所以肯定要对其进行描述,程序运行时就知道了,原来你是初始值为 0 的全局变数呀,然后就在 SRAM 区给你分配了一段固定区域的地址。
    • 对于局部变量来说, 会自动分配大小,不用我们管。SRAM 中一段特定的区域是运行 ZI-data 数据, RW-DATA+ZI-DATA 就是程序运行总共会占用 SRAM 的长度,生成局部变量的栈空间包含在 ZI-data 区的范围。

参考链接: